C语言-判断一条单链表有没有环

来源:互联网 发布:数据科学与工程学院 编辑:程序博客网 时间:2024/05/29 10:11

方法一:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct node_t
{
    struct node_t *next;
    int data;

}node_t;

node_t * Getlink(node_t *head, int *group, int count)
{
    int cout = count;
    int *pgroup = group;
    node_t *new_node = NULL;
    node_t *ptr = NULL;

    assert(group);
    
    for( ; cout > 0; cout--)
    {
        new_node = (node_t *)malloc(sizeof(node_t));
        new_node->next = NULL;
        new_node->data = *pgroup++;
        
        if(NULL == head)
        {
            head = new_node;
            ptr = head;
        }
        else
        {
            ptr->next = new_node;
            ptr = new_node;
        }
    }

    ptr = NULL;
    pgroup = NULL;

    return head;
}

node_t * Getcirclelink(node_t *head, int *group, int count)
{
    int cout = count;
    int *pgroup = group;
    node_t *new_node = NULL;
    node_t *ptr = NULL;

    assert(group);

    for( ; cout > 0; cout--)
    {
        new_node = (node_t *)malloc(sizeof(node_t));
        new_node->next = NULL;
        new_node->data = *pgroup++;

        if(NULL == head)
        {
            head = new_node;
            ptr = head;
        }
        else
        {
            ptr->next = new_node;
            ptr = new_node;
        }
    }
    ptr->next = head->next;

    ptr = NULL;
    pgroup = NULL;

    return head;
}

void Printlink(node_t *head)
{
    while(1)
    {
        if(NULL != head)
        {
            printf("%d\n",head->data);
            head = head->next;
        }
        else
        {
            break;
        }
        sleep(1);
    }
}

void Checklist(node_t *head)
{
    node_t *runfast = NULL;
    node_t *runslow = NULL;

    assert(head);

    //快指针
    runfast = head;
    //慢指针
    runslow = head;

    while(1)
    {
        if(NULL != runfast->next)
        {
            runfast = runfast->next->next;
        }
        else
        {
            runfast = runfast->next;
        }
        runslow = runslow->next;

        if(NULL == runfast)
        {
            runslow = NULL;
            printf("this link is no circle!\n");
            break;
        }
        if((runfast == runslow) && (NULL != runfast))
        {
            printf("fast point catch slow point data is %d, %d\n",runfast->data,runslow->data);
            printf("this link have a circle!\n");
            runslow = NULL;
            runfast = NULL;
            break;
        }
    }
}

int main(void)
{
    int ret = 0;
    int groupf[] = {1,2,3,4,5,6,7};
    int groups[] = {1,2,3,4,5,6,7,8,9};
    node_t *fhead = NULL;
    node_t *shead = NULL;

    //获取一个无环链表
    fhead = Getlink(fhead, groupf, 7);

    //获取一个有环链表
    shead = Getcirclelink(shead, groups, 9);
    

    //判断两条链表有没有环    

    Checklist(fhead);
    Checklist(shead);

    //打印两条链表
    Printlink(fhead);
    Printlink(shead);

    return 0;
}


方法二:

问题:

微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。
问题扩展:

2.如果需要求出俩个链表相交的第一个节点列?


答案:

参考文章:http://blog.csdn.net/lihappy999/article/details/7330175

[cpp] view plaincopy
  1. //20121127  
[cpp] view plaincopy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. typedef struct node     
  5. {    
  6.     int num;    
  7.     node* next;    
  8. }node;    
  9. void generateList(const bool circleTrue,node *&head1,node *&head2);  
  10. //产生链表,两个链表相交,circleTrue 为true表示产生环,false为不带环  
  11. //产生的链表结构肯定是相同的  
  12. bool judgeCircle(node* const head);//判断是否存在环,true存在,false不存在   
  13. node* crossNode(const bool circleTrue,node *head1,node *head2);  
  14. int main()  
  15. {  
  16.     node* head1;  
  17.     node* head2;  
  18.     generateList(true,head1,head2);//相交,且有环  
  19.     bool b=judgeCircle(head1);  
  20.     node* p=crossNode(b,head1,head2);  
  21.     cout<<"cross node:"<<p->num<<endl;  
  22.     return 0;  
  23. }  
  24. void generateList(const bool circleTrue,node *&head1,node *&head2)  
  25. {  
  26.     //每个链表都具有10个节点    
  27.     node *pre1=NULL;    
  28.     node *p=new node;    
  29.     head1=p;    
  30.     p->num=rand();    
  31.     p->next=NULL;    
  32.     pre1=p;    
  33.   
  34.     node *pre2=NULL;    
  35.     p=new node;    
  36.     head2=p;    
  37.     p->num=rand();    
  38.     p->next=NULL;    
  39.     pre2=p;  
  40.   
  41.     //假设从第3个节点开始相交    
  42.     int crossID=3;    
  43.     //假设环的开始节点为5  
  44.     int circleStart=5;  
  45.     node *cs1=NULL;  
  46.     node *cs2=NULL;  
  47.   
  48.   
  49.     for (int i=0;i<10;++i)    
  50.     {    
  51.         //第一条链表的节点    
  52.         p=new node;    
  53.         pre1->next=p;    
  54.         p->num=rand();    
  55.         p->next=NULL;    
  56.         pre1=p;    
  57.         if (i==circleStart)  
  58.         {  
  59.             cs1=p;  
  60.         }  
  61.         if (i==crossID)  
  62.         {  
  63.             cs2=p;  
  64.         }  
  65.     }  
  66.     if (circleTrue)  
  67.     {  
  68.         pre1->next=cs1;  
  69.     }  
  70.   
  71.     for (int i=0;i<5;++i)  
  72.     {  
  73.         p=new node;    
  74.         pre2->next=p;    
  75.         p->num=rand();    
  76.         p->next=NULL;    
  77.         pre2=p;  
  78.     }  
  79.     pre2->next=cs2;  
  80. }  
  81.   
  82. bool judgeCircle(node* const head)    
  83. {    
  84.     //两个指针,一个每次走一步,一个每次走两步,当前一个指针赶上后一个指针时,就说明存在环    
  85.     node* p1=head;    
  86.     node* p2=head;    
  87.     while ((p1->next!=NULL)&&(p2->next!=NULL))    
  88.     {    
  89.         p1=p1->next;    
  90.         p2=p2->next->next;    
  91.         if (p1==p2)    
  92.         {    
  93.             break;    
  94.         }    
  95.     }    
  96.     if (p2->next==NULL)    
  97.     {    
  98.         return false;    
  99.     }    
  100.     else    
  101.     {    
  102.         return true;    
  103.     }    
  104. }    
  105. node* crossNode(const bool circleTrue,node *head1,node *head2)  
  106. {  
  107.       
  108.     if (!circleTrue)  
  109.     {  
  110.         //不存在环  
  111.         int num1=1;  
  112.         int num2=1;  
  113.         node* p1=head1;  
  114.         node* p2=head2;  
  115.         while (head1->next!=NULL)  
  116.         {  
  117.             num1++;  
  118.             head1=head1->next;  
  119.         }  
  120.         while (head2->next!=NULL)  
  121.         {  
  122.             num2++;  
  123.             head2=head2->next;  
  124.         }  
  125.         int preNum=num1>=num2?num1-num2:num2-num1;//对齐处理  
  126.         if (num1>=num2)  
  127.         {  
  128.             for (int i=0;i<preNum;i++)  
  129.             {  
  130.                 p1=p1->next;  
  131.             }  
  132.         }  
  133.         else  
  134.         {  
  135.             for (int i=0;i<preNum;i++)  
  136.             {  
  137.                 p2=p2->next;  
  138.             }  
  139.         }  
  140.         while (p1!=p2)  
  141.         {  
  142.             p1=p1->next;  
  143.             p2=p2->next;  
  144.         }  
  145.         return p1;  
  146.     }  
  147.     else  
  148.     {  
  149.         //存在环  
  150.         //分两种情况,入环口相同和入环口不同  
  151.         node* p1=head1;  
  152.         node* p2=head1;  
  153.         p1=p1->next;  
  154.         p2=p2->next->next;  
  155.         while (p1!=p2)  
  156.         {  
  157.             p1=p1->next;  
  158.             p2=p2->next->next;  
  159.         }  
  160.         //相遇点p2  
  161.         p1=head1;  
  162.         node* entraNode;  
  163.         while (p1!=p2)  
  164.         {  
  165.             p1=p1->next;  
  166.             entraNode=p2;  
  167.             p2=p2->next;  
  168.         }  
  169.         //相遇点p1的前一点pre即为环的入口点  
  170.           
  171.         p1=head1;  
  172.         p2=head2;  
  173.         int num1=0;  
  174.         while (p1->next!=entraNode)  
  175.         {  
  176.             num1++;  
  177.             p1=p1->next;  
  178.         }  
  179.         int num2=0;  
  180.         while (p2->next!=entraNode)  
  181.         {  
  182.             num2++;  
  183.             p2=p2->next;  
  184.         }  
  185.         p1=head1;  
  186.         p2=head2;  
  187.         int preNum=num1>=num2?num1-num2:num2-num1;//对齐处理  
  188.         if (num1>=num2)  
  189.         {  
  190.             for (int i=0;i<preNum;i++)  
  191.             {  
  192.                 p1=p1->next;  
  193.             }  
  194.         }  
  195.         else  
  196.         {  
  197.             for (int i=0;i<preNum;i++)  
  198.             {  
  199.                 p2=p2->next;  
  200.             }  
  201.         }  
  202.         while (p1!=p2)  
  203.         {  
  204.             p1=p1->next;  
  205.             p2=p2->next;  
  206.         }  
  207.         return p1;  
  208.     }  
  209. }
这段代码没问题,完全能够正确判断出链表中是否有环存在。
学习的时候要注意真正理解其中的原理~

假设该链表在环出现之前有L个结点,环中有C个结点。
再假设慢指针初始化时指向的位置为a、快指针初始化时指向的位置为b,
如果二者在t次移动后相遇,也就是说:(a+t-L) mod C == (b+2*t-L) mod C
稍微变形一下就可以看出,t==a-b(mod C)这个模线性方程一定有解
所以无论a、b的起始位置如何,二者总是会相遇的。

这种方法之所以能够判断链表中是否有环,是要使快、慢指针在环里转圈的时候总会碰面。

所以快慢指针的步长选择很重要,起始位置倒是无关紧要的事情。



方法三:

要求: 算法中使用的内存数量是一个常数, 即不能因为链表长度的增减使用的内存也增减.  
下面是本人的一个实现:  
struct list{  
int data;  
struct list *next;  
};  
int has_circle(struct list *head)  
{  
struct list *cur1 = head;  
int pos1 = 0;  
while(cur1){  
struct list *cur2 = head;  
int pos2 = 0;  
pos1 ++;  
while(cur2){  
pos2 ++;  
if(cur2 == cur1){  
if(pos1 == pos2)  
break;  
else  
return 1; //has circle  
}  
cur2 = cur2->;next;  
}  
cur1 = cur1->;next;  
}  
return 0;  
}  

0 0
原创粉丝点击