(版本一)虚拟头节点法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
# 创建虚拟头部节点以简化删除过程
dummy_head = ListNode(next = head)
# 遍历列表并删除值为val的节点
current = dummy_head
while current.next:
if current.next.val == val:
current.next = current.next.next
else:
current = current.next
return dummy_head.next
这段代码实现了一个从单链表中删除特定值节点的功能,它使用了“虚拟头节点法”。下面我将逐步解释代码中的每个部分及其细节。
代码详细解释
-
类和方法定义:
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
这里定义了一个
ListNode
类,表示单链表的节点。每个节点包含两个属性:val
(节点的值)和next
(指向下一个节点的引用)。class Solution: def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
Solution
类中定义了removeElements
方法,该方法接受链表的头节点head
和要删除的值val
。 -
创建虚拟头节点:
dummy_head = ListNode(next=head)
创建一个
dummy_head
(虚拟头节点),它的next
指向原始链表的head
。使用虚拟头节点可以简化处理头节点本身可能被删除的情况。 -
遍历链表:
current = dummy_head while current.next:
- 这里使用一个
current
指针来遍历链表,从dummy_head
开始。 while current.next:
只要current.next
不为None
,就继续遍历链表。
- 这里使用一个
-
检查并删除节点:
if current.next.val == val: current.next = current.next.next else: current = current.next
- 在每次循环中,检查
current.next
(即下一个节点)的值是否等于val
。 - 如果相等,则通过修改
current.next
的指向,跳过当前节点。这一操作实现了节点的删除。 - 如果不相等,则移动
current
到下一个节点继续检查。
- 在每次循环中,检查
-
返回新链表的头节点:
return dummy_head.next
最后,返回从
dummy_head
后面开始的新链表的头节点。这样就避免了返回原头节点可能被删除的情况。
小细节
-
虚拟头节点的好处:
- 它使得链表的删除逻辑一致,无论删除的是头节点还是其他节点。
- 通过始终从虚拟头节点开始,可以方便地处理所有可能的删除情况。
-
保留中间状态:
- 使用
current
指针来保留链表的状态,避免遍历过程中直接修改原始链表,增加了安全性和可读性。
- 使用
-
考虑链表为空的情况:
- 如果输入的链表
head
就是None
(空链表),代码仍然能正确工作,dummy_head
指向None
,返回的也是None
。
- 如果输入的链表
整体上,这段代码提供了一种简洁且高效的方式来删除链表中所有指定值的节点,利用了虚拟头节点来简化边界情况处理。
使用虚拟头节点(dummy_head
)能够在删除节点时避免返回原头节点?
关键点
-
虚拟头节点的作用:
dummy_head
是一个额外的节点,它的next
属性指向原始链表的头节点head
。因此,dummy_head
本身并不包含任何有效的数据,只是为了方便操作。
-
遍历过程:
- 在遍历链表时,
current
指针从dummy_head
开始。通过current.next
来访问链表中的节点。 - 当我们删除一个节点时,实际上是通过修改
current.next
的指向来实现的。例如:current.next = current.next.next
- 这行代码并不会改变
dummy_head
本身的引用。它只是在改变dummy_head
的next
指向的内容。
- 在遍历链表时,
-
返回新链表的头节点:
- 当遍历结束后,我们返回
dummy_head.next
。如果原链表的头节点被删除,dummy_head.next
会指向下一个有效节点(或者是None
,如果所有节点都被删除)。 - 如果原链表的头节点没有被删除,
dummy_head.next
依然指向原链表的头节点。
- 当遍历结束后,我们返回
示例
假设我们有一个链表 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6
,我们想删除值为 6
的节点。
-
创建
dummy_head
:dummy_head -> 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6
-
遍历链表时,
current
从dummy_head
开始:current.next.val
是1
,不删除,移动到2
current.next.val
是2
,不删除,移动到6
current.next.val
是6
,删除,current.next
指向3
- 继续遍历,删除下一个
6
-
最终链表变为:
dummy_head -> 1 -> 2 -> 3 -> 4 -> 5
-
返回
dummy_head.next
,即新链表的头节点1
。
结论
dummy_head
的存在使得我们可以在删除节点时不必担心原头节点的情况,因为dummy_head
作为一个额外的节点始终保持不变。- 通过返回
dummy_head.next
,我们能确保返回的是更新后的链表头节点,而不是原来的头节点。即使原头节点被删除,dummy_head
仍然有效地指向了新的头节点。