思路
怎么一步步穿针引线?
排好序的链表,重复节点会相邻出现,遍历节点时,如果 head.val == head.next.val,就删掉 head.next。即让 head 指向 head.next.next
然后,作为重复结点中的第一个,head 结点也要被删掉。
所以要维护一个 head 的上一结点 prev,用来删 head,即让 prev 取代 head 指向 head.next
接着让 head 推进,考察下一个 head 和 head.next
经过这波循环,我们删掉了与 head 结点值重复的结点。然后在下一轮循环中继续上面操作
上面是遇到重复结点的情况,prev 会去 “跟上” head。
如果没有遇到重复结点呢?head 要步进,考察下一个 head,注意 prev 也要步进,“跟上” head
迭代版 golang
func deleteDuplicates(head *ListNode) *ListNode { // 创建虚拟头结点 dummy := new(ListNode) // 让虚拟头结点指向head dummy.Next = head // 让prev初始指向虚拟头结点 prev := dummy // 开启遍历 for head != nil && head.Next != nil { // 如果出现重复结点 if head.Val == head.Next.Val { // 开启循环删结点 for head.Next != nil && head.Val == head.Next.Val { head.Next = head.Next.Next } // 此时head也要删,让prev指向head.Next prev.Next = head.Next } else { // 如果head和head.Next不是重复结点,则prev步进,head也步进 prev = prev.Next } // 不管head和head.Next是不是重复结点,head都要步进 head = head.Next } return dummy.Next }
递归法
deleteDuplicates 函数的定义是“治理”头结点为 head 的链表,返回出新的链表
我们遍历结点,跳过重复的结点,来到不重复的结点,那么上面这个“大问题”的答案就等于 deleteDuplicates(不重复的结点)
它们是规模不一样的同一问题。
如果没有遇到重复的结点呢,那么当前的 head 是需要保留的,然后 head.Next = deleteDuplicates(head.Next)
func deleteDuplicates(head *ListNode) *ListNode { // 特判 if head == nil || head.Next == nil { return head } // 开启循环 if head.Val == head.Next.Val { for head.Next != nil && head.Val == head.Next.Val { head = head.Next } return deleteDuplicates(head.Next) } else { head.Next = deleteDuplicates(head.Next) return head } }