【程序员面试金典】02.08. 环路检测

1.题目

给定一个有环链表,实现一个算法返回环路的开头节点。
有环链表的定义:在链表中某个节点的next元素指向在它前面出现过的节点,则表明该链表存在环路。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.题解

这里需要对题目做出解释,因为这道题题目描述的十分迷糊,反正我没读懂。
理解起来是这样的,首先pos参数是无用的,代码中的参数也只有一个,所以我们不管pos,下面对示例1做出解释。

没有环路的链表是这样的:
3->2->0->4->null
这里输入head = [3,2,0,-4], pos = 1,表示输入的链表是这样的:
3->2->0->4
   ^     |
   |_____|

思路:使用快慢指针法证明链表是否存在环,若存在,求出环路开头节点
快慢指针法::快指针一次移动两个结点,慢指针一次移动一个结点。假设链表有环,那么两个指针一定会相遇。如果理解不了可以想象两个人从宿舍出发,前往操场跑步。宿舍前往操场的路是直的,没有环,但是操场是有环的。两个人由于一个跑得快,一个跑得慢,他们肯定会在操场上相遇,并且相遇时,跑得快的人比跑得慢的人快了整整一圈。

在这里插入图片描述
证明:如上图所示,a是起点,b是环的入口,c是俩个指针相遇点,ab之间距离为x,bc之间距离是 y。

当slow 走到 b 时,由于fast 比 slow 走的快,所以fast 已经从 b 开始在环上走了 x 步,可能是多余一圈,距离 b 还差 y 步(解释:我们知道俩个指针相遇的点在c,我们让 slow 退回到b,fast 则会退回 2y 步,所以就是距离b 还差y 步,不会可以画图模拟一下)。知道了 fast 距离 b 还差 y 步,上面知道了 fast 从 b 开始在环上走了 x步。所以环的长度为 x+y。所以当俩个指针相遇时,把 fast 放回原点,slow 在 c点不动。然后每次都走一步,下一次相遇的时候就是b 点环的入口。

来源:https://leetcode-cn.com/problems/linked-list-cycle-lcci/solution/lian-biao-kuai-man-zhi-zhen-sao-miao-zheng-ming-by/

class Solution
{
public:
    ListNode *detectCycle(ListNode *head)
    {
        if (!head || !head->next) return 0;
        ListNode *first = head, *second = head;

        while (first && second)
        {
            first = first->next;
            second = second->next;
            if (second) second = second->next;
            else return 0;

            if (first == second)
            {
                first = head;
                while (first != second)
                {
                    first = first->next;
                    second = second->next;
                }
                return first;
            }
        }
        return 0;
    }
};

扩展:
快慢指针的应用
判断单链表是否为循环链表

让快慢指针从链表头开始遍历,快指针向前移动两个位置,慢指针向前移动一个位置;如果快指针到达NULL,说明链表以NULL为结尾,不是循环链表。如果 快指针追上慢指针,则表示出现了循环。

fast=slow=head;
fast=fast->next->next;
slow=slow->next;
whiletrue{
if (fast==NULL || fast->next==NULL)
        return false;
    else if (fast==slow || fast->next==slow)
        return true;
    else{
        fast=fast->next->next;
        slow=slow->next;
    }
}

在有序链表中寻找中位数
该方法在不借助计数器变量实现寻找中位数的功能。原理是:快指针的移动速度是慢指针移动速度的2倍,因此当快指针到达链表尾时,慢指针到达中点。程序还要考虑链表结点个数的奇偶数因素,当快指针移动x次后到达表尾(1+2x),说明链表有奇数个结点,直接返回慢指针指向的数据即可。如果快指针是倒数第二个结点,说明链表结点个数是偶数,这时可以根据“规则”返回上中位数或下中位数或(上中位数+下中位数)的一半。

while (fast&&slow)
      
{
      if (fast->next==NULL)
          return slow ->data;
      else if (fast->next!= NULL && fast->next->next== NULL)
          return (slow ->data + slow ->next->data)/2;
      else
          {
          fast= fast->next;
          fast= fast->next;
          slow = slow ->next;
    }
      
}

发布了13 篇原创文章 · 获赞 4 · 访问量 539

猜你喜欢

转载自blog.csdn.net/weixin_44556968/article/details/105425155
今日推荐