刷题也刷了一段时间了,但是总是找不到写的东西,但就在今天,重新做这道JzOffer上的题,发现思路看着很简单,但是其中的细节倒是不少,错了很多次,所以想写下这道题一起学习一下。
删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
1,1,1,1,1,1,2 1,1,1,1,1,1,1 1,1,2,2,3,3,4,4
public ListNode deleteDuplication(ListNode pHead){
if (pHead == null) {
return null;
}
//返回的头结点
ListNode copyNode = pHead;
ListNode preNode = null;
ListNode nextNode = pHead.next;
while (nextNode != null) {
//如果相等
if (pHead.val == nextNode.val) {
int value = pHead.val;
while (nextNode.next != null && nextNode.next.val == value) {
nextNode = nextNode.next;
}
//获得重复结点的下一个结点(注意下面多个结点也可能是相同的)(1,1,2,2,3,3,4,4)
//跳出的两种情况:
pHead = nextNode.next;
//1.为空(nextNode.next == null),到达链表尾部,又有两种情况
if (pHead == null) {
//当preNode为空,也就是说前面的结点都是重复的(可能是一组,也可能是多组)
//没有不同结点,返回空
if (preNode == null) {
return null;
}
//preNode不为空,结束(注意这里不能再nextNode = pHead.next,pHead已经为空了,会抛异常)
preNode.next = pHead;
return copyNode;
}
//2.值不相等时(nextNode.next.val != value)
else {
//当preNode不为空时,连接
if (preNode != null) {
preNode.next = pHead;
}
else {
//preNode为空时,什么都不做(可能后面也是重复的)(1,1,2,2,3,3,4,4)
//这里是判断next是不是为空
//如果为空后不做处理,直接向下运行(因为这里是preNode为空,直接向下,漏掉最后一个数据)
if (pHead.next == null) {
preNode = pHead;
copyNode = pHead;
}
}
}
}
//如果不等,preNode就不为空
else {
//第一次,可能头结点已经改变,赋值给copyNode
if (preNode == null) {
preNode = pHead;
copyNode = preNode;
continue;
}
preNode = pHead;
pHead = pHead.next;
}
nextNode = pHead.next;
}
//如果pHead没有动,那说明只有一个节点
if (pHead == copyNode) {
return copyNode;
}
//preNode为null,说明链表中一个不同的都没有(preNode改变是在pHead和pNext不等的时候)
if (preNode == null) {
return null;
}
return copyNode;
}
public ListNode deleteDuplication(ListNode pHead) {
//这里pHead.next==null的情况,如果交给后面会出问题
//与上面的做对比可以把后面的未移动的判断加在这里
if(pHead==null||pHead.next==null){
return pHead;
}
ListNode preNode = null;
ListNode pNode = pHead;
ListNode pRNode = pHead;
while (pNode!=null){
ListNode pNext = pNode.next;
//分两种情况
//1.相等 2.不相等
//如果pNext!=null&&pNext.val==pNode.val,去重
if(pNext!=null&&pNext.val==pNode.val){
int value = pNode.val;
//保存pNode
ListNode pToBeDelete = pNode;
while (pToBeDelete!=null&&pToBeDelete.val==value){
//移动pNext
pNext = pToBeDelete.next;
pToBeDelete = pNext;
}
}
//如果pNext为null或值不相等(与我的想法一致,只有不相等时才赋予preNode值)
else {
preNode = pNode;
}
//也是两种情况
//1.preNode为空 2.preNode不为空
//只有当不同时,才会改变preNode值
//相同时会一直赋值,最后直到null
if(preNode==null){
//对比上面改变copyNode的值
//一直跟着条件在动,筛选
pRNode = pNext;
}
else {
preNode.next = pNext;
}
pNode = pNext;
}
return pRNode;
}
对比来看的话,我的思路无疑复杂了很多,我认为原因就是最开始就去纠结了很多细节,没有先把一个问题的总体框架,通用的方法想到,只是注重了细节的很多种情况,最后导致显得很复杂。先是分析大类问题,不要在小细节上纠结这是我对比后的想法。
总的来说最开始时,需要想到的两类情况:
1.结点值相不相同 (只有结点不同时才能改变preNode)
2.preNode是否为空
先把总体的通用框架在心中有个大概,再在通用框架中填充细节
这个解法的主要思路就是在最开始就增加一个节点,避免后面关于preNode的判断
public ListNode deleteDuplication(ListNode pHead){
//新建节点,作为最开始的preNode
ListNode addHeadNode = new ListNode(-1);
addHeadNode.next = pHead;
ListNode realHead = addHeadNode;
ListNode preNode = addHeadNode;
while (realHead != null) {
ListNode nextNode = realHead.next;
//同样的判断值是否相同
if(nextNode != null && realHead.val == nextNode.val){
ListNode pToBeDelete = nextNode;
int value = realHead.val;
while (pToBeDelete != null && pToBeDelete.val == value) {
nextNode = pToBeDelete.next;
pToBeDelete = nextNode;
}
}
//这里略微有区别,先连接,在赋值
else {
preNode.next = realHead;
preNode = realHead;
}
//不用进行preNode为空的判断,永远都不为空
preNode.next = nextNode;
realHead = nextNode;
}
//返回我们创建头结点的下一个结点
return addHeadNode.next;
}