zoukankan      html  css  js  c++  java
  • 判断单链表是否有环及相关引申问题

    主要有如下问题:

    1.判断单链表是否有环

    2.求环的长度

    3.找出环的入口节点

    4.求带环链表长度

    思路:

    1. 判断是否有环,首先想到的思路是利用Hashmap存储访问过的节点,然后每次访问下一个节点前先判断节点是否在Hashmap中存在。

    一旦存在,则存在环。这种解法需要格外的存储空间,并不是最优解。

    另一种思路是追赶法,用两个指针ptr_fast,ptr_slow同时指向链表的头部,ptr_slow一次移动一步,ptr_fast一次移动两步,如果最终ptr_slow和ptr_fast重合则说明链表有环,如果ptr_fast走到空指针(链表的结尾)则说明链表无环。

     

    证明如下:

    设ptr_fast每次走X步,ptr_slow单位时间走Y步;链表环内的节点数是C,链表环外节点数是Q,我们有

    X*t-Y*t = n*C  --> t= n*C / (X-Y) -->需保证t(多少次)是正整数,所以 X-Y = 1。

    指针移动的次数为 (X+Y)*t = (X+Y)*n*C / (X-Y) -->使移动次数最少, 即X+Y最少,所以X=2,Y=1

    2. 求环的长度,只要ptr_fast,ptr_slow在环中再走一次,再次相遇所需要的操作数便是环的长度。需注意,由于X = 2Y,所以再次相遇的点与首次相遇的点是同一个节点。

    3. 找出环的入口节点。有定理:相遇点到入口点的距离 = 头节点到入口点的距离,因此令ptr_fast重新指向链表的头节点,然后ptr_fast和ptr_slow同时一次移动一步,当ptr_slow和ptr_fast再次重合时该节点指针就是环的入口节点指针。

    定理证明如下:

    当ptr_fast若与ptr_slow相遇时,ptr_slow肯定没有走遍历完链表,而ptr_fast已经在环内循环了n圈(1<=n)。假设ptr_slow走了s步,则ptr_fast走了2s步(ptr_fast步数还等于s 加上在环上多转的n圈),设环长为r,则:
    2s = s + nr
    s= nr

    设整个链表长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)

    (L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点。

    4. 求整个带环链表长度。根据第2问环的长度和第3问头结点到环入口的距离,即可得到整个带环链表的长度。

    代码:

      1 #include "stdafx.h"
      2 #include <iostream>
      3 
      4 using namespace std;
      5 
      6 //the length of circum
      7 const int length = 22;
      8 //define the starting node of loop
      9 const int circularNum = 11;
     10 
     11 struct node{
     12     int value;
     13     struct node *next;
     14 };
     15 
     16 
     17 /** Create linklist containing nodes with value from 0 to length
     18 * parameter: num, which the last pointer points to, in order to create circular linklist
     19 */
     20 node *createCircularLinklist(int num){
     21     
     22     node *head,*last,*p,*temp;
     23     head=(node *)malloc(sizeof(node));
     24     last=(node *)malloc(sizeof(node));
     25 
     26     for(int i=0;i<length;i++){
     27         p=(node *)malloc(sizeof(node));
     28         p->value = i;
     29         p->next = NULL;
     30         if(i==0) 
     31             head->next = p;
     32         if(i==num) 
     33             temp = p; //if(i=num) temp = p; low-level mistake! 
     34         last->next = p;
     35         last = p;
     36     }
     37 
     38     last->next = temp; //last points to the temp to create circum
     39 
     40     return head->next;
     41 }
     42 
     43 //print linklist
     44 void traverse(node *ptr){
     45     int count = 0;
     46     while(ptr!=NULL){
     47         if(count>length) break;
     48         cout<<ptr->value;
     49         if(ptr->next!=NULL) cout<<" -> ";
     50         ptr=ptr->next;
     51         count++;
     52     }
     53     cout<<endl;
     54 }
     55 
     56 /**
     57 * 1.Judge if the linklist is circular
     58   2.Find the length of circum and the linklist
     59   3.Find the starting node
     60 */
     61 int isCircular(node *list){
     62     bool isLoop = false;
     63     if(list==NULL) return -1;
     64     node *ptr_fast,*ptr_slow;
     65     ptr_fast = list;
     66     ptr_slow = list;
     67 
     68     while(ptr_fast->next && ptr_fast->next->next){
     69         ptr_fast = ptr_fast->next->next;
     70         ptr_slow = ptr_slow->next;
     71 
     72         if(ptr_fast==ptr_slow){
     73             isLoop = true;
     74             cout<<"It's Circular!"<<" Meeting at: "<<ptr_fast->value<<endl;
     75             break;
     76         }
     77     }
     78 
     79     if(!isLoop){
     80         cout<<"It's not Circular!"<<endl;
     81         return -1;
     82     }
     83 
     84     //Find the length of circular
     85     int length = 0;
     86     while(ptr_fast->next && ptr_fast->next->next){
     87         ptr_fast = ptr_fast->next->next;
     88         ptr_slow = ptr_slow->next;
     89         length++;
     90         if(ptr_fast==ptr_slow){
     91             cout<<"The length: "<<length<<" Meeting at: "<<ptr_fast->value<<endl;
     92             break;
     93         }
     94     }
     95 
     96     //Find the starting node of circular
     97     ptr_fast = list;
     98     int len = 0;
     99     while(ptr_fast!=ptr_slow){
    100         ptr_fast = ptr_fast->next;
    101         ptr_slow = ptr_slow->next;
    102         len++;
    103     }
    104     cout<<"Starting node: "<<ptr_fast->value<<endl;
    105     cout<<"lenght of link: "<<len+length<<endl;
    106 
    107 
    108 }
    109 
    110 int _tmain(int argc, _TCHAR* argv[])
    111 {
    112     node *list = createCircularLinklist(circularNum);
    113     traverse(list);
    114     isCircular(list);
    115     return 0;
    116 }
  • 相关阅读:
    小程序-自定义组件
    51Nod
    CodeForces
    JSON、闭包和原型----透视Javascript语言核心
    转载:动态规划法总结
    to初学者:从汉诺塔问题深入理解递归算法思想
    不知‘时间复杂度’所云的看过来
    盲点流水账记录
    常用序列化协议总结
    排序——了解总体以及插入排序
  • 原文地址:https://www.cnblogs.com/techyc/p/2679706.html
Copyright © 2011-2022 走看看