删除链表中重复的结点——Java实现1 前言

刷题也刷了一段时间了,但是总是找不到写的东西,但就在今天,重新做这道JzOffer上的题,发现思路看着很简单,但是其中的细节倒是不少,错了很多次,所以想写下这道题一起学习一下。

2 题目2.1 题目描述

删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

2.1 测试数据

1,1,1,1,1,1,2 1,1,1,1,1,1,1 1,1,2,2,3,3,4,4

3 自己的实现
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;
    }
4 JzOffer思路
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是否为空

先把总体的通用框架在心中有个大概,再在通用框架中填充细节

5 新的思路

这个解法的主要思路就是在最开始就增加一个节点,避免后面关于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;
    }