此题涉及一个重要概念:哨兵结点。
哨兵节点广泛应用于树和链表中,通常以伪头、伪尾、特殊标记等形式存在。它存在的目的通常是使链表永不为空、永不无头、方便改变链表结构、或实现头部可删等。
这道题的思路是,写一个循环使head前进的同时一步步反转链表中的元素。具体思考过程如下:
首先,给链表加一个伪头(哨兵结点)dummy,这个结点指向原链表的表头。
然后,以链表[1→2→3→4→5]为例,进行三个改变链的指向的操作,如下图所示。
之后,链表就变成了[2→1→3→4→5]。然后head前进,重复这三个操作。
此时,链表就变成了[3→2→1→4→5]。然后head继续前进,重复,直到循环结束。
这里的箭头大家看了最好自己画一画体会一下,不然不是特别好理解。
Python3的代码实现如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
dummy = ListNode(0) # 这里dummy的value可以任意取,我取了0
dummy.next = head # 指向原链表的头,相当于加了一个伪头,这个dummy就是哨兵结点
while(head != None and head.next != None):
dnext = dummy.next
hnext = head.next
# 下面的三行代码分别表示上面提到的三个操作
dummy.next = hnext
head.next = hnext.next # hnext.next是当前结点的下下个结点
hnext.next = dnext
return dummy.next
这里有两个点要注意一下。
- 循环里前两行先提前保存了dummy指向的位置和head指向的位置,是因为在链表操作的过程中,这些值都会产生变化,而我们希望用到的是变化前的值。如果全部用dummy.next来写这个代码的话,循环根本出不去..
- 初学者有时候分不清head和head.val的区别。这里稍作解释:head表示链表中一个结点的位置,这里面包括了value(值)属性和next(指向)属性;而head.val只表示一个数值。
最终的执行用时击败60%,内存消耗击败45%。
另外在LeetCode评论区看到了另一个非常厉害的解题方法,来自gaussic大神。这个方法巧妙地利用了Python3在多元赋值时等号右边值不会随赋值而改变的性质,完美解决了上面我用两行代码、两个额外空间才解决的问题。太妙了!看的我直呼内行...
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
p, rev = head, None
while p:
rev, rev.next, p = p, rev, p.next
return rev
最终的执行用时击败82%,内存消耗击败54%。