【数据结构基础/经典面试Oj题】单向链表经典Oj题目第二弹之环形链表(环形链表的证明加做题思路加逻辑分析加代码实操,一应俱全的汇总)

Singly-Linked List Oj - Circle - list

条件

  * Definition for singly-linked list.
  * struct ListNode {
  *     int val;
  *     struct ListNode *next;
  * };
  */

环形链表

成环链表与不成环链表相比,有一个很明显的差异

  • 成环链表中是不存在尾节点的
  • 所有节点中的指针都是指向下一节点的

根据这个差异,便可以很好的分辨链表分辨出来
该如何利用这个差异呢?

  • 若在一个成环链表中寻其尾节点,一定会出现死循环
  • 但设计程序时,也不能将程序设计成死循环。如果利用快慢指针,便可以很好的解决这个问题。

给定一个链表,判断链表中是否有环

 

来源:力扣(LeetCode
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii

思路

我们需要做的就是判断是否有环,所以可以定义一个fast 和 slow快慢指针,快指针每次走两步,慢指针每次走一步,如果快指针可以走到NULL,即无环,如果快指针一直走但是走不到头,就是有环,需要判断的是,快指针必定会比慢指针先多走一圈而追上慢指针,即相等。

代码

 bool hasCycle(struct ListNode *head)
 {
     struct ListNode *fast = head, *slow = head;
     //fast->next和fast都应该不为空才行
     while (fast && fast->next)
    {
 ​
         fast = fast->next->next;
         slow = slow->next;
         if (fast == slow)
        {
             return true;
        }
    }
     return false;
 }

延伸问题

为什么slow和fast一定在环中相遇,会不会在环里面错过,永远遇不上,证明一下。

  • 带环则一定会相遇
  • 分析证明:

slow和fast的距离差始终为n,n为slow走过的步数,n从1开始增加,为正整数。每追一次,距离减少1,他们之间的距离最后都会减到0,这个点就是相遇的点。

为什么slow走一步,fast走两步呢,能不能fast走n步呢,证明一下。

如果slow走一步,fast走三步,那么每次slow走一步,它们会相差2步,slow刚进环之后假设距离差为N,如果N为奇数,则距离会变为N-2,N-4,...,1,-1。-1就代表他们之间的距离为C-1(C为环长)。

  • 如果C-1为奇数,那么就永远追不上了
  • 如果C-1为偶数,那么可以追上。

假设slow一次走一步,fast一次走x步,以此类推

  • 那么就是(x-1)步为每次的差值,假设slow刚进环的二者距离差为N,N-n(x-1),如果N是(x-1)的倍数,就可以追上。如果不是,比如N = n(x-1)-1,就是代表他们两个此刻的距离为C-1,如果C-1是(x-1)的倍数,就可以追上,否则,无法追上。

  • 总结一下:

  • 如果N是步数差的整数倍,可以追上。
  • 如果第一次追不上,那么会出现fast掠过slow但并不相遇。可能会出现从-1、-2、...、-(x-2)的情况。
  • 当环长C加上上面的负数为步数差的整数倍时,也可以追上。

给定一个链表,返回链表开始入环的第一个节点

延伸问题

slow一次走一步,fast一次走两步,一定会相遇,如何求环的入口点呢?

 

  • 结论:

    • 一个指针从相遇点开始走,另一个指针从链表头开始走(都是每次走一步),它们会在环的入口点相遇。

    • 两个指针同时从链表头开始走,第一次相遇时,走过的路程差就是环长C的整数倍。

思路1

用上面的结论,即先找相遇点->再双指针求交点,找到的交点就是换的入口点。

代码1

 struct ListNode *detectCycle(struct ListNode *head)
 {
     struct ListNode *slow = head, *fast = head;
     while(fast&&fast->next)
    {
         fast = fast->next->next;
         slow = slow->next;
         if(slow == fast)
        {
             struct ListNode *meetNode = slow;
             while(meetNode!=head)
            {
                 meetNode = meetNode->next;
                 head = head->next;
            }
             return meetNode;
        }
    }
     return NULL;
 }

思路2

我们把相遇点断开,让其指向空,会出现两个链表,一个是由原链表头指向相遇点,一个是由原来相遇点的后一个节点指向相遇点的链表。让两个链表求交点。交点即为环节点。(实践起来稍繁琐。)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值