【LeetCode】关于链表的环、相交等问题

【LeetCode】关于链表的环、相交等问题

1、链表环形

LeetCode上关于环形链表有两个题:

  • Q141–easy
    • 内容:判断一个链表是否有环。
  • Q142–medium
    • 内容:判断一个链表是否有环,并返回链表环形开始的第一个节点。

题目141思路分析:

​ 判断一个链表是否有环,可以使用“快慢指针”的方法,快指针一次走两格即fast = fast.next.next,慢指针一次走一格即slow = slow.next。可以分为两种情况:

  1. 无环:即遍历到最后,肯定是快指针fast == null

  2. 有环:快指针先进入环,慢指针后进入。并且可以确定,快慢指针相遇时,慢指针没有走完一圈或刚好走完一圈。

    因为在同一个环中fast和slow之间的距离不会大于环的长度,并且两者的距离一直在逐渐减小。

    • 开始的时候,slow = 3 , fast = 0;
    • slow = 2,fast = 2 -->刚好是在入口相遇。
      在这里插入图片描述
      在这里插入图片描述

    注:左图为网图,本人只是在上进行了简单加工以及文字描述。

    public boolean hasCycle(ListNode head) {
              if (head == null || head.next == null){
                return false;
            }
            ListNode fast = head;
            ListNode slow = head;
    
            while (slow != null && fast.next != null){
                slow = slow.next;
                fast = fast.next.next;
                //代表无环
                if (fast == null){
                    return false;
                }
                //代表有环
                if (slow == fast){
                    return true;
                }
            }
            return  false;
        }
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJNbcese-1639139015244)(C:\Users\qiuzt\AppData\Roaming\Typora\typora-user-images\image-20211210192443083.png)]

题目142思路分析:

​ 相比上一个题目,增加了一个确定环开始的起点。

​ 解题步骤分为两步:一是确定链表有环,二才是确定链表环的起始点。此题的难点是第二步。从题目141可以知道,当fast和slow相遇时候,slow还没有走完链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Px4MpCmp-1639139015245)(C:\Users\qiuzt\AppData\Roaming\Typora\typora-user-images\image-20211210193658155.png)]

从上面的分析知道,当fast和slow相遇时,slow还没有走完链表,假设fast已经在环内循环了n(1<= n)圈。假设slow走了s步,则fast走了2s步,又由于

fast走过的步数 = s + n*r(s + 在环上多走的n圈),则有下面的等式:

2*s = s + n * r ; (1)

=> s = n*r (2)

如果假设整个链表的长度是L,入口和相遇点的距离是x(如上图所示),起点到入口点的距离是a(如上图所示),则有:

a + x = s = n * r; (3) 由(2)推出

a + x = (n - 1) * r + r = (n - 1) * r + (L - a) (4) 由环的长度 = 链表总长度 - 起点到入口点的距离求出

a = (n - 1) * r + (L -a -x) (5)

集合式子(5)以及上图我们可以看出,从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。

因此我们就可以分别用一个指针(ptr1, prt2),同时从head与slow和fast的相遇点出发,每一次操作走一步,直到ptr1 == ptr2,此时的位置也就是入口点!

其实换句话说就是,当fast和slow再次相遇的时候,就是环的起始点了。别忘了慢指针得从头节点开始哦,上面说的就是,从相遇点起,接着往下走到环起始点的长度,和头节点到环起始点的长度是相等的。

public ListNode detectCycle(ListNode head) {
       ListNode fast = head;
       ListNode slow = head;
       boolean isLoop = false; //双指针找出有环或者无环

        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if(fast == slow){
                isLoop = true;
                break;
            }
        }
        if(isLoop){
            slow = head;
            while(fast != null && fast.next != null){
                if(slow == fast){
                    return slow;
                }
                slow = slow.next;
                fast = fast.next;
            }
        }
        return null;
    }
2、链表是否相交

LeetCode题目160,是一道简单题。

在这里插入图片描述

可以看到题目的最后一题的描述,即我们只要在返回结果之前,把链表回归原始结构就行。这个暗示有点明显!!!

这道题目前我会的有两种形式的解法:

  1. 把A链表的首尾相连,使AB两条链表组成一个环形链表,然后题目间接的就变成了Question142,但是需要注意返回结果之前,需要将链表恢复原来的结构。

  2. 类似于环形结构,只不过是AB两条链表先走自己的链表,走到最后,再走对方的链表,当然每次都的时候都需要比较结点是否相等。如果相等,那么说明相交。

    ​ 可以这么理解,先自己走的路,然后再走对方的路,如果相交的话,肯定会在入口相遇。

    ​ 假如A、B两个链表相交,相交链表的长度为Y,A不相交的链表长度为X,B不相交的链表长度为Z,按照上面A走的长度为X+Y+Z,B走的长度为Z+Y+X,两者相等。

    public ListNode getIntersectionNode(ListNode headA, ListNode headB){
        //令某单链表直接成环
        ListNode curHeadA = headA;
        while(true){
            if(curHeadA.next != null){
                curHeadA = curHeadA.next;
            }else{
                curHeadA.next = headA;
                break;
            }
        }
        //双指针问题
        ListNode slow = headB;
        ListNode fast = headB;
    
        while(slow != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                break;
            }
            if(slow == null || fast == null){
                curHeadA.next = null;
                return null;
            }
        }
        if(slow == null || fast.next == null){
            curHeadA.next = null;
            return null;
        }
        ListNode node1 = headB;
        ListNode node2 = slow;
        while(node1 != node2){
            node1 = node1.next;
            node2 = node2.next;
        }
    }
    
    public ListNode getIntersectionNode(ListNode headA, ListNode headB){
        if(headA == null || headB == null){
            return null;
          }
    
        ListNode nodeA = headA;
        ListNode nodeB = headB;
    
        while(nodeA != nodeB){
            nodeA = nodeA == null ? headB : nodeA.next;
            nodeB = nodeB == null ? headA : nodeB.next;
        }
        return nodeA;
    }
    

部分内容参考:
https://www.cnblogs.com/yorkyang/p/10876604.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值