正题
删除排序链表中的重复元素 II
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表。
示例1:
输入: head = [1,2,3,3,4,4,5]
输出: [1,2,5]
示例2:
输入: head = [1,1,1,2,3]
输出: [2,3]
与前文 删除链表中重复的元素 相比,完全删除了出现多次的元素,包含第一次出现也会被删除,这样以来难度稍微提升了一点,因为我们需要多删除一次首次出现的情况。可以参考上文,做一次删除,然后记录下删除的元素,再一次遍历,再将同 val 值首次出现的情况删除掉即可完成。
但是这样没有创新,并且需要二次遍历链表。如何做到只遍历一次链表,删除所有重复的元素(包含首次出现的那一次)呢?
由题可以知道,链表是升序的,那么链表中的相同的元素一定是相邻的
能用图的绝不打字,下面试图用一张动图来表达我的思路:
总体思路:
1.定义一个 head 作为 firstPre 节点,为什么要定义 firstPre 呢,因为链表是单向的,如果发现了重复,想要删掉当前的就需要重新遍历,如果在当前节点前面再定一个节点,就可以方便删除当前节点。
2.比较 first (当前节点) 和 next (下一个节点),由于链表的递增性,相同的节点必然是相邻的,也就是 first 和 next 可能会出现重复,所以比较 first 和 next
3.如果不相同,那么 firstPre 向右移动,这样会产生新的 first 和 next (first 为 firstPre.next , next 为 first.next)
4.一直比较 first 和 next ,如果相同,那么删除 next (将 first 指向 next.next),接着标记first,继续比较.这里可能会有两个疑问:(1)为什么要标记first?因为相同,那么 讲道理first节点也是要被删除的,当前仅删除了next节点,之所以没有删除它,那是因为要考虑可能会出现3个相同的连在一起,如果删除了first,第二个重复的就无法找到了。(2)为什么firstPre不右移了?同理,需要考虑到是否会存在3个相同的节点相邻,右移会导致疏漏。
5.如果依然相同,继续4操作
6,出现不同时,执行3操作,并且删除标记节点。
show my code:
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var deleteDuplicates = function(head) {
let temp = new ListNode(0)
temp.next = head
let firstPre = temp
let isDeleteFirst = false
do {
console.log(firstPre)
const first = firstPre ? firstPre.next : p
if (first === null) {
break
}
const next = first.next
if (next && first.val === next.val) {
// 删除 next, 标记 first
isDeleteFirst = true
first.next = next.next
continue
} else {
if (isDeleteFirst) {
firstPre.next = first.next
isDeleteFirst = false
} else {
firstPre ? firstPre = firstPre.next : firstPre = head
}
}
} while (firstPre)
return temp.next
};