今天刷链表。
迭代
很简单,保存cur.next,然后cur -> prev。
def reverseList(self, head: ListNode) -> ListNode:
cur = head
prev = None
while cur:
next = cur.next
cur.next = prev
prev = cur
cur = next
return prev
递归
要用递归,先想子问题。比如我正序遍历的时候,对于每个节点,假设后面所有节点已经被反转了,然后进行操作即可。
注意本题有两个特殊情况:空链表和只有一个元素的链表。
画出转换之后的依赖链路,然后顺着该链路的顺序搭建新的链表。
def swapPairs(self, head: ListNode) -> ListNode:
cur = head
prev = None
res = head if head else None
while cur and cur.next:
third = cur.next.next
if prev:
prev.next = cur.next
else:
res = cur.next
cur.next.next = cur
cur.next = third
prev = cur
cur = third
return res
看到“倒数”,先快速撸了一个用栈的解法:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
stack = []
cur = head
while cur:
stack.append(cur)
cur = cur.next
next = None
while n > 1:
next = stack.pop()
n -= 1
cur = stack.pop()
if cur == head:
return next
stack.pop().next = next
return head
当然最好的解法还是快慢指针,我又用virtual head优化了一下:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
vhead = ListNode()
vhead.next = head
left = vhead
right = vhead
while (n > 1):
right = right.next
n -= 1
leftprev = vhead
while right.next:
leftprev = left
left = left.next
right = right.next
leftprev.next = left.next
return vhead.next
一遍过,很合理,因为吸取了上一题的经验。
我的双指针解法如下:扫两遍,第一遍让两个指针都停在链表的终点。如果相交,那么它们的终点应该是同一node。这次遍历还记录下两条链表的长度。第二遍让长的链表的指针多跑两条链表的差值,然后两个指针齐头并进走到相交的节点。
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA or not headB:
return None
pa = headA
pb = headB
counta = 1
countb = 1
while pa and pa.next:
counta += 1
pa = pa.next
while pb and pb.next:
countb += 1
pb = pb.next
if pa != pb:
return None
if counta >= countb:
for _ in range(counta - countb):
headA = headA.next
else:
for _ in range(countb - counta):
headB = headB.next
while headA != headB:
headA = headA.next
headB = headB.next
return headA
但是题解的方法更烧。和判断有环链表的快慢指针一样,找到不变量(这题中是a+b+c),无需增加限制,两个指针自然会走到一起。
这道题很棒,包含了链表双指针的很多知识。
知道了这三个前提条件,再去看题解就能明白了。
链表主要考察的是指针操作。
简单的链表题(比如反转链表),最好掌握迭代和递归两种方法。
复杂的链表题基本上要涉及双指针。双指针问题中一定要找出恒等条件。19是建立两个指针的恒等距离差;160是找到a+c+b=b+c+a;142是三个原则。