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


解法1:

  如果链表中要删除某个节点,我们需要找到这个节点的前一个节点,否则在删除时会断开。因此使用一个pre来记录之前的节点。
过程:在寻找过程中,我们肯定是比较当前的节点和当前节点的下一个节点的值。
  这里举例2-3-4-5-5-5-5-6-6-7。pre应该记录4,而cur应当直到了6,然后进行处理。特殊情况是头结点会重复。因此也要拿出来处理。具体分析过程在代码的注释中了。
  这里没有使用虚拟头结点或者辅助头结点。如果用了虚拟头结点的话,下面的判断是不是头节点重复的步骤可以省略,少两行代码。但是最后需要返回的是虚拟头结点的next,才是真实的头结点。

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        ListNode pre = null;//头结点的前驱是Null
        ListNode cur = pHead;
        while(cur!=null){
            if(cur.next!=null && cur.next.val==cur.val){//相等时
                while(cur.next!=null && cur.next.val==cur.val)
                    cur=cur.next;//我们要找下一个不重复的,防止某个节点出现了3次或者更多
//以2-3-4-5-5-5-5-6-6-7而言,此时cur指向了5,而cur.next是6,因此我们再移动,让cur指向6
                cur=cur.next;
                if(pre==null)//这两行是对头结点重复的处理,更新头节点(如2-2-2-3-4-5)
                    pHead=cur;
                else//如果不是重复的节点,就是常规的重复,就将4和6连接起来。
                    pre.next=cur;
            }else{//此时是不相等,相应的进行移动
                pre=cur;
                cur=cur.next;
            }
        }
        return pHead;
    }
}

虚拟头结点的写法

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null) return null;
        // 构建辅助头结点
        ListNode head = new ListNode(Integer.MIN_VALUE);//保证赋值和任何节点值都不重复
        head.next = pHead;
        ListNode pre = head;
        ListNode cur = head.next;
        while(cur!=null){
            if(cur.next != null && cur.next.val == cur.val){
                while(cur.next != null && cur.next.val == cur.val){
                    cur = cur.next;
                }
                cur = cur.next;
                pre.next = cur;
            }else{
                pre = cur;
                cur = cur.next;
            }
        }
        return head.next;//虚拟头结点的next才是真实头结点
    }
}

解法2:

递归,不用考虑头结点的情况。

public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        if (pHead == null || pHead.next == null) { 
            return pHead;
        }
        if (pHead.val == pHead.next.val) { // 当前节点是重复节点
            ListNode node = pHead.next;
            while (node != null && node.val == pHead.val) {
                // 跳过值与当前节点相同的全部节点,找到第一个与当前节点不同的节点
                node = node.next;
            }
            // 从不重复的结点继续递归,并且这个节点也是上次递归的返回值
            return deleteDuplication(node); 
        } else {
            //继续递归下一个节点。返回的是不重复的节点,因此直接连起来就好
            pHead.next = deleteDuplication(pHead.next); 
            return pHead;
        }
    }
}

总结:

  一刚开始也想过用TreeSet,不过想来想去,还是用正统的解法吧。毕竟本题考察的就是去重这一过程,使用Set太虚了,在面试的时候不讨好。本题递归的写法,在链表重复元素较少时,性能比较低。对于我个人来说,解法1比递归容易看懂,总之就是推荐掌握解法1.