zoukankan      html  css  js  c++  java
  • 判断一个链表是否有环的几种方法

    一、单链表是否有环 
    思路分析: 
    单链表有环,是指单链表中某个节点的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> 于是从链表头与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。

  • 相关阅读:
    mysql 中 时间函数 now() current_timestamp() 和 sysdate() 比较
    在spring boot 中使用itext和itextrender生成pdf文件
    dockerfile构建的镜像
    在linux环境下使用itext生成pdf
    在spring data jpa中使用自定义转换器之使用枚举转换
    Sping Boot返回Json格式自定义
    【强化学习RL】model-free的prediction和control — MC, TD(λ), SARSA, Q-learning等
    【强化学习RL】必须知道的基础概念和MDP
    【GAN与NLP】GAN的原理 —— 与VAE对比及JS散度出发
    【NLP】使用bert
  • 原文地址:https://www.cnblogs.com/zk-blog/p/12558129.html
Copyright © 2011-2022 走看看