链表(二) 找链表倒数第k个结点(详细文字推导) + 链表环(详细通俗思路)

链表(二) 找链表倒数第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个结点。 🐔

19. 删除链表的倒数第 N 个结点

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 
        

876. 链表的中间结点

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

142. 环形链表 II

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 步,还会再次相遇,画图可以容易得到这个不变性,并且这时候再次相遇的这一点就是环的起点!!

160. 相交链表

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

加法顺序不变性,轻松理解这题

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值