个人评价:链表类题目。。。难点在于指针和头尾处理,调试真难 😂
题目难度:🌟 🌟 (参考 LeeCode 简单🌟 、中等🌟 🌟 、困难🌟 🌟 🌟 )
另,通常算法题会考察分析时间复杂度和空间复杂度,可参考笔者之前的文章
删除排序链表中的重复元素 II
题目描述
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中没有重复出现 的数字。
返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
提示:
链表中节点数目在范围 [0, 300] 内 -100 <= Node.val <= 100 题目数据保证链表已经按升序排列
解题思路
采用遍历,思路如下:
- 当前值与之后的值对比,不同 =》继续(已经排序,可确定唯一)
- 当前值与之后的值对比,相同 =》继续循环判断与下下个节点值是否相同
注意加入 哨兵节点/哑结点 用于可能的头结点删除处理
存在内循环,可考虑使用递归,个人认为此题无需引入,会增加理解的复杂性
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil {
return nil
}
// 引入 哨兵节点/哑结点 减少头结点删除的难度
dummy := &ListNode{0, head}
cur := dummy
for cur.Next != nil && cur.Next.Next != nil {
if cur.Next.Val == cur.Next.Next.Val {
x := cur.Next.Val
for cur.Next != nil && cur.Next.Val == x { // 循环删除,直至不同
cur.Next = cur.Next.Next
}
} else {
cur = cur.Next
}
}
return dummy.Next
}
引申
一开始看错了。。。没有看清楚是排序链表,故使用了 Hash 记录,并且只做了去重,类似 uniq,并没有删除重复的数字本身(还在想中等题,确定么?😂)
func deleteDuplicates(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
prev := head
curr := head
nMap := make(map[int]bool)
for curr.Next != nil {
next := curr.Next
if _, ok := nMap[curr.Val]; ok { // 如果存在,删除节点
*curr = *curr.Next // 删除链表节点(不可为尾)
continue
}
nMap[curr.Val] = true
prev = curr
curr = next
}
if _, ok := nMap[curr.Val]; ok { // 尾结点删除,特殊处理
prev.Next = nil
}
return head
}
总结
引入哨兵结点,在处理链表题还是很好用的。另外,题多看几遍 😂