判断一个链表是否有环的几种方法

一、单链表是否有环 
思路分析: 
单链表有环,是指单链表中某个节点的next指针域指向的是链表中在它之前的某一个节点,这样在链表的尾部形成一个环形结构。判断链表是否有环,有以下几种方法。

1 // 链表的节点结构如下
2 typedef struct node
3 {
4     int data;
5     struct node *next;
6 } NODE;
(1)最常用方法:定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形链表;如果走得快的指针走到了链表的末尾(next指向 NULL)都没有追上第一个指针,那么链表就不是环形链表。
 1 // 判断链表是否有环
 2 bool IsLoop(NODE *head) // 假设为带头节点的单链表
 3 {
 4     if (head == NULL)
 5         return false;
 6
 7     NODE *slow = head->next; // 初始时,慢指针从头节点开始走1步
 8     if (slow == NULL)
 9         return false;
10
11     NODE *fast = slow->next; // 初始时,快指针从头节点开始走2步
12     while (fast != NULL && slow != NULL) // 当单链表没有环时,循环到链表尾结束
13     {
14         if (fast == slow)
15             return true;
16
17         slow = slow->next; // 慢指针每次走一步
18
19         fast = fast->next;
20         if (fast != NULL)
21             fast = fast->next;
22     }
23
24     return false;
25 }

(2)通过使用STL库中的map表进行映射。首先定义 map<NODE *, int> m; 将一个 NODE * 指针映射成数组的下标,并赋值为一个 int 类型的数值。然后从链表的头指针开始往后遍历,每次遇到一个指针p,就判断 m[p] 是否为0。如果为0,则将m[p]赋值为1,表示该节点第一次访问;而如果m[p]的值为1,则说明这个节点已经被访问过一次了,于是就形成了环。

 1 #include <iostream>
 2 using namespace std;
 3 #include <map>
 4
 5 // 使用STL中的map来判断单链表中是否有环
 6 map<NODE *, int> m;
 7 bool IsLoop_2(NODE *head)
 8 {
 9     if (head == NULL)
10         return false;
11
12     NODE *p = head;
13
14     while (p)
15     {
16         if (m[p] == 0) // 一般默认值都是0
17             m[p] = 1;
18         else if (m[p] == 1)
19             return true;
20
21         p = p->next;
22     }
23
24      return false;
25 }

(3)不推荐使用:定义一个指针数组,初始化为空指针,从链表的头指针开始往后遍历,每次遇到一个指针就跟指针数组中的指针相比较,若没有找到相同的指针,说明这个节点是第一次访问,还没有形成环,将这个指针添加到指针数组中去。若在指针数组中找到了同样的指针,说明这个节点已经访问过了,于是就形成了环。

二、若单链表有环,如何找出环的入口节点。 
步骤: 
<1> 定义两个指针p1和p2,在初始化时都指向链表的头节点。 
<2> 如果链表中的环有n个节点,指针p1先在链表上向前移动n步。 
<3> 然后指针p1和p2以相同的速度在链表上向前移动直到它们相遇。 
<4> 它们相遇的节点就是环的入口节点。 
那么如何得到环中的节点数目?

可使用上述方法(1),即通过一快一慢两个指针来解决这个问题。当两个指针相遇时,表明链表中存在环。两个指针相遇的节点一定是在环中。可以从这个节点出发,一边继续向前移动一边计数,当再次回到这个节点时,即可得到环中的节点数了。

 1 // 1、先求出环中的任一节点
 2 NODE *MeetingNode(NODE *head) // 假设为带头节点的单链表
 3 {
 4     if (head == NULL)
 5         return NULL;
 6
 7     NODE *slow = head->next; // 初始时,慢指针从头节点开始走1步
 8     if (slow == NULL)
 9         return NULL;
10
11     NODE *fast = slow->next; // 初始时,快指针从头节点开始走2步
12     while (fast != NULL && slow != NULL) // 当单链表没有环时,循环到链表尾结束
13     {
14         if (fast == slow)
15             return fast;
16
17         slow = slow->next; // 慢指针每次走一步
18
19         fast = fast->next;
20         if (fast != NULL)
21             fast = fast->next;
22     }
23
24     return NULL;
25 }
26
27 // 2、从已找到的那个环中节点出发,一边继续向前移动,一边计数,当再次回到这个节点时,就可得到环中的节点数了。
28 NODE *EntryNodeOfLoop(NODE *head)
29 {
30     NODE *meetingNode = MeetingNode(head); // 先找出环中的任一节点
31     if (meetingNode == NULL)
32         return NULL;
33
34     int count = 1; // 计算环中的节点数
35     NODE *p = meetingNode;
36     while (p != meetingNode)
37     {
38         p = p->next;
39         ++count;
40     }
41
42     p = head;
43     for (int i = 0; i < count; i++) // 让p从头节点开始,先在链表上向前移动count步
44         p = p->next;
45
46     NODE *q = head; // q从头节点开始
47     while (q != p) // p和q以相同的速度向前移动,当q指向环的入口节点时,p已经围绕着环走了一圈又回到了入口节点。
48     {
49         q = q->next;
50         p = p->next;
51     }
52
53     return p;
54 }

备注:在MeetingNode方法中,当快慢指针(slow、fast)相遇时,slow指针肯定没有遍历完链表,而fast指针已经在环内循环了n(n>=1)圈。假设slow指针走了s步,则fast指针走了2s步。同时,fast指针的步数还等于s加上在环上多转的n圈,设环长为r,则满足如下关系表达式:

2s = s + nr; 
所以可知:s = nr; 
假设链表的头节点到“环的尾节点“的长度为L(注意,L不一定是链表长度),环的入口节点与相遇点的距离为x,链表的头节点到环入口的距离为a,则满足如下关系表达式: 
a + x = s = nr; 
可得:a + x = (n - 1)r + r = (n - 1)r + (L - a) 
进一步得:a = (n - 1)r + (L -a - x) 
结论: 
<1> (L - a -x)为相遇点到环入口节点的距离,即从相遇点开始向前移动(L -a -x)步后,会再次到达环入口节点。 
<2> 从链表的头节点到环入口节点的距离 = (n - 1) * 环内循环 + 相遇点到环入口点的距离。 
<3> 于是从链表头与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。

原文地址:https://www.cnblogs.com/zk-blog/p/12558129.html

时间: 2024-12-10 06:53:36

判断一个链表是否有环的几种方法的相关文章

判断单链表是否有环的两种方法(转)

如图,如果单链表有环,则在遍历时,在通过6之后,会重新回到3,那么我们可以在遍历时使用两个指针,看两个指针是否相等. 方法一:使用p.q两个指针,p总是向前走,但q每次都从头开始走,对于每个节点,看p走的步数是否和q一样.如图,当p从6走到3时,用了6步,此时若q从head出发,则只需两步就到3,因而步数不等,出现矛盾,存在环方法二:使用p.q两个指针,p每次向前走一步,q每次向前走两步,若在某个时候p == q,则存在环. #include <stdio.h> #include <st

判断单链表是否有环的两种方法

如图,如果单链表有环,则在遍历时,在通过6之后,会重新回到3,那么我们可以在遍历时使用两个指针,看两个指针是否相等. 方法一:使用p.q两个指针,p总是向前走,但q每次都从头开始走,对于每个节点,看p走的步数是否和q一样.如图,当p从6走到3时,用了6步,此时若q从head出发,则只需两步就到3,因而步数不等,出现矛盾,存在环 方法二:使用p.q两个指针,p每次向前走一步,q每次向前走两步,若在某个时候p == q,则存在环. 代码如下: 1 #include <stdio.h> 2 #inc

判断一个链表是否有环

思路:如果开始有两个指针指向头结点,一个走的快,一个走的慢,如果有环的话,最终经过若干步,快的指针总会超过慢的指针一圈从而相遇. 如何计算环的长度呢?可以第一次相遇时开始计数,第二次相遇时停止计数. 如何判断环的入口点?碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点.头指针开始走,相遇的那个点就是连接点. 当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈.假设slow走了s步,那么fast走了2s步.fast的步数还等于s走的加上

用javascript判断一个html元素是否存在的五种方法:

1. 判断表单元素是否存在(一) if("periodPerMonth" in document.theForm){ return true; }else{ return false; } 2. 判断页面元素是否存在 if(document.getElementById("XXX")){ //存在 } 3. 判断表单元素是否存在(二) if(document.theForm.periodPerMonth){ //存在 } 或 if(typeof(document.t

判断单向链表是否有环,环起点,环长,链表长

今天在微信上看到一篇介绍如何判断单向链表是否有环的文章,感觉很有意思,整理一下读后的思路. 一.判断单向链表是否有环 方法1:设置一个Hashset,顺序读取链表中的节点,判断Hashset中是否有该节点的唯一标识(ID).如果在Hashset中,说明有环:如果不在Hashset中,将节点的ID存入Hashset. 这种方法时间复杂度已经最优,但是因为额外申请了Hashset,所以空间复杂度不算最优. 方法2:设置2个指针,指向头节点.第1个指针每次指向下一个节点:第2个指针指向下一个节点的下一

如何判断单链表是否存在环

原文:http://blog.csdn.net/liuxialong/article/details/6555850 如何判断单链表是否存在环 给定一个单链表,只给出头指针h: 1.如何判断是否存在环? 2.如何知道环的长度? 3.如何找出环的连接点在哪里? 4.带环链表的长度是多少? 解法: 1.对于问题1,使用追赶的方法,设定两个指针slow.fast,从头指针开始,每次分别前进1步.2步.如存在环,则两者相遇:如不存在环,fast遇到NULL退出. 2.对于问题2,记录下问题1的碰撞点p,

11.判断单链表是否有环

判断单链表是否有环:   这里也是用到两个指针,如果一个链表有环,那么用一个指针去遍历,是永远走不到头的.   因此,我们用两个指针去遍历:first指针每次走一步,second指针每次走两步,如果first指针和second指针相遇,说明有环.时间复杂度为O (n). 方法 // 方法:检测单链表是否有环 public boolean hasCycle(Node head) { if (head == null) { return false; } Node first = head; Nod

判断单链表是否有环及寻找环的入口

一.判断单链表是否有环 1.遍历链表 遍历链表,将已经访问过的结点,设置为已访问,如果访问同一结点两次,则说明有环,时间O(n),空间O(n). 2.链表反转 遍历链表,将访问的结点指针反向.如果存在环,反转后next指向原链表头,但链表反转会破坏链表的结构,反转需要注意缓存当前结点的下一结点,时间O(n),空间(1). 3.快慢指针 设置快慢两个指针fast和slow,两个指针都链表头开始,fast每次移动2步,slow为1步.如果存在环,则fast先进入环,slow后进入环,最后两者在环中相

判断单链表是否有环相关问题(转载加总结)

给定一个单链表,只给出头指针h: 1.如何判断是否存在环? 2.如何知道环的长度? 3.如何找出环的连接点在哪里? 4.带环链表的长度是多少? 解法: 1.对于问题1,使用追赶的方法,设定两个指针slow.fast,从头指针开始,每次分别前进1步.2步.如存在环,则两者相遇:如不存在环,fast遇到NULL退出. 2.对于问题2,记录下问题1的碰撞点p,slow.fast从该点开始,再次碰撞所走过的操作数就是环的长度s. 3.问题3:有定理:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分