环形链表

时间:2024-05-07 10:22:41

  学会的环形链表的约瑟夫问题,下面来两道力扣中的环形链表问题
  题目链接如下:环形链表。题目如下:
在这里插入图片描述
在这里插入图片描述
  我们要判断一个链表看其是否有环,先说结论,我们可以利用快慢指针进行求解。顾名思义,快慢指针就是存在两个指针,一个走的快,一个走的慢,快指针一次走走两步或三步四步都可以,慢指针一般走一步。
  这道题中,我们让快指针一次性走两边,慢指针一次性走一步,当慢指针进入环以后,快指针开始追慢指针,如果快指针能追上慢指针,也就是两指针相遇,则证明该链表带环。 代码如下:

/**
 1. Definition for singly-linked list.
 2. struct ListNode {
 3.     int val;
 4.     struct ListNode *next;
 5. };
 */
typedef struct ListNode ListNode; 
bool hasCycle(struct ListNode *head) {
    ListNode* slownode = head, *quicknode = head;//定义快慢指针
    while(quicknode && quicknode->next)//快指针以及快指针指向的下一个节点不为空,则一直循环
    {
        slownode = slownode->next;//慢指针一次走一步
        quicknode = quicknode->next->next;//快指针一次走两步
        if(quicknode == slownode)//快慢指针相遇,则链表带环
        {
            return true;
        }
    }
    return false;//为空跳出循环说明该链表不带环
}

下面将解决大家的疑惑点:

  1. 为什么快慢指针一定会相遇,而不是会错过,如何证明
  2. 当慢指针走一步,快指针走3步,4步……又一定能追上相遇吗,如何证明

  这两个问题我们来一一解决。
  首先是第一个问题:为什么快慢指针一定会相遇,而不是会错过,如何证明。我们可以将快慢指针走的过程图画出来
在这里插入图片描述
  所以当快指针一次走两步,慢指针一次走一步时,如果链表带环,二者一定会在环内相遇,这就证明解决了第一个问题:
  下面是第二个问题:当慢指针走一步,快指针走3步,4步……又一定能追上相遇吗,如何证明。
  当慢指针一次走一步,快指针一次走3步时,证明如下:

当slow进环时,fast和slow的距离为n
slow一次走一步,fast一次走三步
则它们每追击一次,距离缩小2,

n是偶数 n是奇数
n n
n-2 n-2
…… ……
4 3
2 1
0 -1
追上了 错过了,进行新一轮追击

如果n是奇数,fast错过了slow,此时fast到了slow前面一格,假设环的长度为C,则slow和fast是距离变为了C-1,开始新一轮追击,还是讨论C-1是偶数还是奇数的问题,如果C-1是偶数,就能追上,如果C-1是奇数,那永远不会追上。

根据以上分析我们可以得出结论:

  • 当n时偶数,第一轮就能追上
  • 当n时奇数时,第一轮追击会错过,二者的距离变成C-1(C是环的长度)
    • 如果C-1是偶数,即C是奇数,那么下一轮就能追上相遇
    • 如果C-1是奇数,即C是偶数,那么永远追不上

看似我们已经讨论出了有追上和追不上的这两种情况,但是追不上的情况:即n是奇数C是偶数的情况真的会存在吗,我们可以继续往下讨论:
在这里插入图片描述

当slow进环时,fast和slow的距离为n
slow一次走一步,fast一次走三步
我们就能发现fast走的距离是slow的三倍
根据这个关系可以列出等式
slow走的距离:L
fast走的距离:L+x * c+c-n(slow进环时,假设fast已经在环里面转了n圈)
则得到等式:3 * L = L+x * c+c-n
化简得到:2 * L = (x+1) * c-n
2*L必定是偶数,
根据上面得出的结论:当n是奇数,c是偶数时,永远追不上
将其带入右边的式子,得到:(x+1)*偶数 - 奇数,此时得到的式子只能是奇数,与左边不等,则发现永远追不上的条件不成立。

  根据以上一系列分析,可以得出结论:
  当slow一次走一步,fast一次走三步时,若n是偶数,则第一轮就能追上,若n是奇数,c是奇数时,第二轮追上,即无论如何,一定能相遇追上。当fast一次走4步或者5步时也是同样的证明方法。