`
liujiahaogood
  • 浏览: 26150 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

【转】如何判断链表是有环的

阅读更多
一种O(n)的办法就是(搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重合,反之亦然):

关于这个解法最形象的比喻就是在操场当中跑步,速度快的会把速度慢的扣圈

可以证明,p2追赶上p1的时候,p1一定还没有走完一遍环路,p2也不会跨越p1多圈才追上

我们可以从p2和p1的位置差距来证明,p2一定会赶上p1但是不会跳过p1的

因为p2每次走2步,而p1走一步,所以他们之间的差距是一步一步的缩小,4,3,2,1,0 到0的时候就重合了

根据这个方式,可以证明,p2每次走三步以上,并不总能加快检测的速度,反而有可能判别不出有环

比如,在环的周长L是偶数的时候,初始p2和p1相差奇数的时候,p2每次走三步,就永远和p1不重合,因为他们之间的差距是:  5, 3 , 1,  L-1, L-3

如下代码:



bool check(const node* head)
{
    if(head==NULL)  return false;
    node *low=head, *fast=head->next;
    while(fast!=NULL && fast->next!=NULL)
    {
        low=low->next;
        fast=fast->next->next;
        if(low==fast) return true;
    }
    return false;
}



既然能够判断出是否是有环路,那改如何找到这个环路的入口呢?

解法如下: 当p2按照每次2步,p1每次一步的方式走,发现p2和p1重合,确定了单向链表有环路了

接下来,让p2回到链表的头部,重新走,每次步长不是走2了,而是走1,那么当p1和p2再次相遇的时候,就是环路的入口了。

这点可以证明的:

在p2和p1第一次相遇的时候,假定p1走了n步骤,环路的入口是在p步的时候经过的,那么有

p1走的路径: p+c = n;         c为p1和p2相交点,距离环路入口的距离

p2走的路径: p+c+k*L = 2*N;   L为环路的周长,k是整数

显然,如果从p+c点开始,p1再走n步骤的话,还可以回到p+c这个点

同时p2从头开始走的话,经过n不,也会达到p+c这点

显然在这个步骤当中p1和p2只有前p步骤走的路径不同,所以当p1和p2再次重合的时候,必然是在链表的环路入口点上。



#include <iostream>
#include <stdlib.h>
using namespace std;

class CList {
public:
int nData;
CList * pNext;
} * pRoot = NULL;

const int size = sizeof(CList) / sizeof(int);

int  buffer[101*size];
bool Init(int n)
{
    pRoot = (CList*)buffer;
    if ( n<1 && n>98 )  return false;
    CList * pTemp = NULL;
   
    for ( int i=0; i<101; i++ ) {
        pTemp = new (buffer+i*size) CList();
        pTemp->nData = i;
        pTemp->pNext = (CList *)(buffer + (i+1)*size);
    };
    pTemp->pNext = (CList *) (buffer + n*size);
    return true;
}

void ClearCircle(CList * pRoot)
{
CList * p1, * p2;
p1 = p2 = pRoot;
do {
     p2 = p2->pNext->pNext;
     p1 = p1->pNext;
} while ( p2!=NULL && p1!=p2);

if ( p1 == p2 ) {
  p2 = pRoot;
  while (1) {
   p2 = p2->pNext;
   if ( p1->pNext == p2 ) break;
   p1 = p1->pNext;
  }
  p1->pNext = NULL;
}
}

int main(int argc, char *argv[])
{
    CList * pList = pRoot;
if (Init(21) )
{
  cout << "Before clear:!" << "\r\n";
  pList = pRoot;
  for ( int i=0; i<104; i++)
  {
   cout << pList->nData << "\r\n";
   pList = pList->pNext;
  }
  ClearCircle(pRoot);
}
cout << "After clear:" << "\r\n";
pList = pRoot;
while (pList) {
  cout << pList->nData << "\r\n";
  pList = pList->pNext;
}
return 0;
}
分享到:
评论

相关推荐

    链表相关问题的完整代码

    **1、判断链表是否有环** **2、寻找环的入口点** **3、计算环的节点数** **4、计算(有环)链表的节点数** **5、找出环中距任意一点最远的节点** **6、判断两个无环链表是否相交** **7、寻找两个链表的相交的节点**

    约瑟夫环问题(数组实现,链表实现

    约瑟夫环问题,也被称为...无论是数组还是链表实现,都需要对数据结构有深入的理解,并能够根据问题特点灵活选择合适的方法。在实际应用中,我们可以根据问题规模、内存限制以及计算性能要求,来决定采用哪种实现方式。

    面试单链表问题总结-反转,逆序输出,判环,求交点

    当快慢指针相遇时,即可确定链表有环。 最后,**寻找两个单链表的交点**问题。假设两个链表有交点,我们需要找到这个交点。一种方法是先分别遍历两个链表,记录各自的长度,然后从较长链表的头节点开始,按照长度差...

    用链表实现分数到小数的转换

    4. **处理循环部分**:当余数再次出现时,表明出现了循环,创建环链表。将余数对应的数字添加到链表末尾,并设置环连接。 5. **输出小数**:对于有限小数,直接遍历链表输出数字;对于无限循环小数,持续遍历环链表...

    链表题目总结1

    - 在判断链表是否相交时,可以用栈存储两个链表的节点,比较栈顶元素来确定交点。 在处理链表问题时,理解链表的基本操作(如遍历、插入、删除)至关重要,同时熟悉不同的算法策略可以帮助我们更高效地解决问题。...

    实用单向链表的基本操作函数24个C语言版

    13. **判断链表是否有环**:使用快慢指针(Floyd's Cycle-Finding Algorithm)来检测链表中是否存在环。 14. **找到环的起始节点**:如果链表有环,可以进一步找到环的起始节点。 15. **节点交换**:交换链表中两...

    C++实现的链表操作源码

    在C++编程语言中,链表是一种...源码可能还包括对链表的其他高级操作,例如查找链表的中间节点、判断链表是否有环、计算链表长度等。理解和掌握这些操作对于提升C++编程技能,特别是数据结构和算法方面的能力至关重要。

    算法面试通关40讲完整课件 05-07 数组、链表

    2. 链表环检测(Linked List Cycle, 如LeetCode的第141题):判断链表中是否存在环,并找出环的起点。 3. 两两交换链表中的节点(Swap Nodes in Pairs, 如LeetCode的第142题):将链表中相邻的节点两两交换。 4. ...

    算法大全-面试题-链表-栈-二叉树-数据结构

    8. **判断单链表是否有环及环的起始点与长度** 使用Floyd判环算法(龟兔赛跑),两个指针分别以1倍和2倍速度前进,若相遇则存在环。确定环的起始点可让一个指针回到头节点,两者再次以相同速度前进,相遇点即为环...

    字节跳动最爱考的 64 道算法题1

    1. 通过快慢指针寻找链表中点 - 这种方法适用于链表长度未知的情况,可以找到链表的中间节点,通常用于解决链表问题,例如判断链表是否有环。 2. 判断回文链表 - 可以通过前序遍历或利用快慢指针找中点后比较的方式...

    九章算法之链表与数组(Linked List & Array)

    检测链表中的环是一个经典的算法问题,通过快慢指针可以有效地解决。复制链表涉及到更复杂的数据结构理解,特别是当链表节点中包含指向其他节点的随机指针时。 数组的操作包括了基础的元素访问、插入、删除等,还...

    算法大全-面试题-链表-栈-二叉树-数据结构.docx

    6. **二级链表转一级链表**:遍历二级链表,将每个节点的子链表链接到一级链表的末尾。 7. **交换任意两个元素**:找到要交换的两个元素,交换它们的Data值,然后调整它们的Next指针,使其指向正确的后继节点。 8....

    JavaScript数据结构之单链表和循环链表

    5. 判断链表状态(isEmpty,size):检查链表是否为空,获取链表长度。 6. 转换为字符串(toString):将链表转换为便于阅读的字符串格式。 7. 访问头尾节点(getHead,getTail):获取链表首尾节点。 以上就是...

    C++将二叉树转为双向链表及判断两个链表是否相交

    如果链表有环,快指针会先到达环内的某个点,然后慢指针会在稍后相遇。如果没有环,快指针会先到达NULL。一旦两个指针相遇,我们就可以沿着慢指针回溯到环的入口,找到相交的第一个节点。 ```cpp ListNode* ...

    数据结构与算法笔记(三)线性表 定义线性表节点的结构.pdf

    对于判断链表是否有环的问题,可以利用快慢指针法,快指针每次前进两个节点,慢指针每次前进一个节点,如果两者相遇则说明链表有环。而对于寻找链表中间节点的问题,同样可以使用快慢指针,快指针速度是慢指针的两倍...

    微软等公司数据结构及算法面试第1-100题汇总

    7. **判断链表是否相交**:如果链表不带环,可以分别遍历两个链表到尾部,记录长度,之后从长度短的链表头部开始,按长度差向另一个链表移动,直到相遇则相交,否则不相交。如果链表可能有环,可以使用 Floyd 环检测...

    数据结构课程设计(joseph环,拓扑排序,纸牌游戏)

    解决Joseph环问题通常使用循环链表,因为它能很好地模拟环形结构。链表的每个节点包含两个属性:密码(即成员的编号)和序号。通过循环遍历链表,找到报数到m的节点并删除,直至链表为空。在这个过程中,记录出列的...

    aaa.rar_无向图 环_无向图所有环_无向图最小环_最小生成树_树所有操作

    对任意给定的图(顶点数不小于20,边数不少于30,图的类型可以是有向图、无向图、有向网、无向网),能够输入图的顶点和边(或弧)的信息,并存储到相应存储结构(邻接矩阵、邻接表、十字链表、邻接多重表,任选其中...

    微软等公司数据结构-算法面试第1-100题汇总

    7. **判断链表相交**: - 对于没有环的链表,可以同时从两个链表的头节点开始遍历,直到它们相遇,说明链表相交。 - 如果链表可能有环,可以使用Floyd判断环的方法,同时使用快慢指针,如果快指针最终追上慢指针,...

Global site tag (gtag.js) - Google Analytics