题目
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
来源Leetcode: https://leetcode-cn.com/problems/remove-linked-list-elements/
分析
遇到这种经典的链表题的时候,我懵逼了,我不会。。。绕头脑,稍微不注意就会绕进去了,就不知道指针指哪里了。所以这题我要好好分析分析,好好记录一下,来理解这种链表。
这个题目啊,要注意是删除给定值的所有节点。注意到我加粗的地方了吗?
我们知道删除链表元素时候,只需要把前一个节点的Next指向当前节点的Next元素,从而就可以丢弃到当前要删除的节点。所以我们要记录上一个节点prev。
假如cur节点是要删除的节点,那么将prev.Next = cur.Next即可实现删除。
但我们知道,删除节点要注意如果删除的是头节点呢。我可以简单的将head = head.Next,但如果有多个连续的呢?那么我们可能要做一个while循环一直检测,一直删除。
这里就有另外一种处理了,做一个伪头,也就是哨兵节点。哨兵节点指向头节点,这样删除头节点就和删除中间节点没什么两样了,就可以简化处理逻辑。
有了哨兵节点,我们就可以pre, cur := sentinel, head
。这样当判定cur是要删除的节点时候,pre.Next = cur.Next
就完成删除了。管你cur是头节点还是尾结点呢?
删除节点,我们要考虑它是不是尾结点吗?不需要,因为啥呢,删除尾结点时候,尾结点的Next是nil,把它赋给prev.Next 并不会产生panic。
解法
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeElements(head *ListNode, val int) *ListNode {
if head == nil {
return nil
}
sentinel := &ListNode{
Next: head,
Val: -1,
}
// pre 用于记录上一个节点,这里首先指向哨兵节点,cur就指向head节点
pre, cur := sentinel, head
for cur != nil {
if cur.Val == val {
// 如果当前节点与要删除的一致,就把上一个节点的Next跳过当前节点,指向下一个
pre.Next = cur.Next
} else {
// 这里的pre只用做记录上一个节点。用于要删除的时候,切换一下指向。
pre = cur
}
cur = cur.Next
}
return sentinel.Next
}