链表(二) 找链表倒数第k个结点(详细文字推导) + 链表环(详细通俗思路)
假设链表共有n个结点, 找到倒数第k个结点 明白找倒数第k个结点,也就是找整数第n-k+1个结点:可以以k = 1举例,倒数第一个,也就是找整数第n - 1 + 1 = n个结点
遍历两遍链表的思路,需要先遍历一遍链表算出 n
的值,然后再遍历链表计算第 n - k + 1
个节点。也就是说,这个解法需要遍历两次链表才能得到出倒数第 k
个节点。
另一种思路就是双指针的思路,在写的时候搞清楚为什么不需要加1
结点数为n,最开始head就指向了第一个结点,例如如果想要走到第3个结点,只需要再走2 步即可。简单来说就是第一个结点向前走k步,走到的是第k + 1
个结点
一、在这里我们希望第二个结点走到正数n - k + 1个结点,也就是再走n - k 步,就走到倒数第k个结点。 这一点非常重要 🤒,
二、一个链表最开始长度为 n, 要走到 null共需要走 n 步,链表的长度不包括null结点
三、综上两点,思路清晰,让第一个结点走k步,走到第k步的时候设置第二个结点, 当第一个结点走到null,这时候第2个结点走了n - k 步,也就是走到了整数第n - k + 1个 结点,也就是找到了倒数第k个结点。 🐔
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(-1)#dummy 结点避免了找整数第一的上一个结点找不到的情况。
dummy.next = head
x = self.findFromEnd(dummy, n + 1)
x.next = x.next.next
return dummy.next #删除倒数第n个结点,找到倒数第n + 1个,让倒数第n + 1 个指向下下个节点就好了。
def findFromEnd(self, head: ListNode, k: int) -> ListNode:
p1 = head
for i in range(k):
p1 = p1.next
p2 = head
while p1:
p2 = p2.next
p1 = p1.next
return p2
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow, fast = head, head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
return slow
这里用双指针来找到中间结点,其中
while fast is not None and fast.next is not None
分别对应链表长度为偶数和奇数的情况。当链表长度为奇数时候,假设n = 5 走到中间结点需要走2步,也就是整数第3个,此时快指针在最后一个结点。那么
fast.next is None
当链表长度为偶数时候,假设n = 6 走到中间结点需要走3步,也就是整数第4个,此时快指针走了6步,也就走到空结点。对应
fast is None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast, slow = head, head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
if fast == slow:
break
if fast is None or fast.next is None:
return None
slow = head
while fast != slow:
slow = slow.next
fast = fast.next
return slow
解体思路:快慢指针都从起始点开始走,假设走了k步相遇,也就是慢指针走的距离是k,快指针走的距离是2k假设 慢指针当前位置,也就是快慢指针相遇位置距离结点起点距离为m, 那么抓住关键点从起点再走k - m 步,和从相遇点再走k - m 步,还会再次相遇,画图可以容易得到这个不变性,并且这时候再次相遇的这一点就是环的起点!!
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
p1, p2 = headA, headB
while p1 != p2:
if p1 is not None:
p1 = p1.next
else :
p1 = headB
if p2 is not None:
p2 = p2.next
else:
p2 = headA
return p1
三元表达式写法
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
p1, p2 = headA, headB
while p1 != p2:
p1 = headB if p1 is None else p1.next #三元组还是更简洁,并且这里只写= 后面的内容就可以了
p2 = headA if p2 is None else p2.next
return p1
解体思路还是再找到针对题目特征的不变量 假设 A = X + Y B = Z + Y 我们要找到Y的起点,这一题的不变量就是
X + Y + Z = Z + Y + X
所以通过这个加法的不变性,遍历过程中A,B会有值相等的时候,那么这个值就是相交入口,或者是None,这时候Y = 0
加法顺序不变性,轻松理解这题