给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

5. 删除排序链表中的重复元素II - 图1

输入: 1->2->3->3->4->4->5 输出: 1->2->5

输入: 1->1->1->2->3 输出: 2->3

dummy

方法一:一次遍历
思路与算法

由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。由于链表的头节点可能会被删除,因此我们需要额外使用一个哑节点(dummy node)指向链表的头节点。

具体地,我们从指针 \textit{cur}cur 指向链表的哑节点,随后开始对链表进行遍历。如果当前 \textit{cur.next}cur.next 与 \textit{cur.next.next}cur.next.next 对应的元素相同,那么我们就需要将 \textit{cur.next}cur.next 以及所有后面拥有相同元素值的链表节点全部删除。我们记下这个元素值 xx,随后不断将 \textit{cur.next}cur.next 从链表中移除,直到 \textit{cur.next}cur.next 为空节点或者其元素值不等于 xx 为止。此时,我们将链表中所有元素值为 xx 的节点全部删除。

如果当前 \textit{cur.next}cur.next 与 \textit{cur.next.next}cur.next.next 对应的元素不相同,那么说明链表中只有一个元素值为 \textit{cur.next}cur.next 的节点,那么我们就可以将 \textit{cur}cur 指向 \textit{cur.next}cur.next。

当遍历完整个链表之后,我们返回链表的的哑节点的下一个节点 \textit{dummy.next}dummy.next 即可。

细节

需要注意 \textit{cur.next}cur.next 以及 \textit{cur.next.next}cur.next.next 可能为空节点,如果不加以判断,可能会产生运行错误。

代码

注意下面 C++ 代码中并没有释放被删除的链表节点以及哑节点的空间。如果在面试中遇到本题,读者需要针对这一细节与面试官进行沟通。

  1. const deleteDuplicates = function (head) {
  2. if (head === null || head.next === null) {
  3. return head;
  4. }
  5. const dummy = new ListNode(-1);
  6. dummy.next = head;
  7. let cur = dummy;
  8. while (cur.next !== null && cur.next.next !== null) {
  9. if (cur.next.val === cur.next.next.val) {
  10. const repeatVal = cur.next.val;
  11. // 每次删除一个cur.next就行了,剩下的后面再删除,
  12. // 不用一次删除很多个
  13. while(cur.next && cur.next.val === repeatVal) {
  14. cur.next = cur.next.next;
  15. }
  16. } else {
  17. cur = cur.next;
  18. }
  19. }
  20. return dummy.next;
  21. };