Chinaunix首页 | 论坛 | 博客
  • 博客访问: 239705
  • 博文数量: 62
  • 博客积分: 973
  • 博客等级: 准尉
  • 技术积分: 530
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-16 23:25
文章分类

全部博文(62)

文章存档

2013年(1)

2012年(14)

2011年(47)

分类: C/C++

2011-12-21 18:32:51

问题:
1、如何判断一个链表是不是有环?
2、如果链表为存在环,如何找到环的入口点?
3、判断两个链表是否相交,若相交求交点。
网上很多:
 
一、判断单链表是否有环
typedef struct LinkedList{
    int data;
    LinkedList* pNext;
}LinkedList;
    bool i***istLoop(LinkedList* pList)
    {
        if (pList == NULL || pList->pNext == NULL)
            return false;
        LinkedList* p1 = pList;
        LinkedList* p2 = pList;
        while (p2 != NULL && p2->pNext != NULL)
        {
            p1 = p1->pNext;
            p2 = p2->pNext->pNext;
            if (p1==p2)
                return true;
        }
        return false;
    }

二、找到环的入口点
    当fast若与slow相遇时,slow肯定没有走遍历完链表。证明:
如图,slow从H走到A时,fast一定在环上某处C(或B或D)。设此时slow走过了HA=x,fast前方距环入口点距离CDA=y,环周长为r。slow继续沿ABC方向前行y,则同时fast也前行了2y刚好追上slow,二者相遇,这时slow还要经过(r-y)才遍历完一个链表总长L。因为y是环上一点距入口A的长度,所以r-y>0,得证。
H________A____B
         |____|
         D    C
当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。
假设slow走了s步,则fast走了2s步(fast步数还等于s加上在环上多转的n圈),设环长为r,则:
2s = s + nr
s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L - a - x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,
于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
程序描述如下:
slist *  FindLoopPort(slist * head)
{
    slist * slow  =  head,  * fast  =  head;
    while  ( fast  &&  fast -> next )
    {
    slow  =  slow -> next;
    fast  =  fast -> next -> next;
    if  ( slow  ==  fast )  break ;
    }
    if  (fast  ==  NULL  ||  fast -> next  ==  NULL)
    return  NULL;
    slow  =  head;
    while  (slow  !=  fast)
    {
    slow  =  slow -> next;
    fast  =  fast -> next;
    }
    return  slow;
}
类似的解释:
|------ n ------|
H____h____A__c__Meet
          |_____|r
         
在p2和p1第一次相遇的时候,假定p1走了n步,环路的入口是在h步的时候经过的,那么有
  p1走的路径: h+c = n;         c为p1和p2相交点,距离环路入口的距离
  p2走的路径: h+c+k*r = 2*n;    r为环路的周长,k是整数
显然,如果从h+c点开始,p1再走n步骤的话,还可以回到h+c这个点
同时p2从头开始走的话,经过n步,也会达到h+c这点
显然在这个步骤当中p1和p2只有前h步走的路径不同,所以当p1和p2再次重合的时候,必然是在链表的环路入口点上。
 
三、判断两个链表是否相交
 
1。可以先分别遍历找出两个链表的尾节点,如果连个尾节点相同,则两个链表相交。程序描述如下:
//找到list1的最后一个节点p1
p1=head1
while(p1->next不是NULL)
    p1=p1->next
找出list2的最后一个节点p2
if(p1==p2)
  相交
else
  不相交
时间复杂度:O(list1.length + list2.length)
空间复杂度:O(1)

2。使用hash表
loop:p1从head1到最后一个节点
把p1放入hash表table中
loop:p2从head2到最后一个节点
if(p2在hash表中)
   相交
时间复杂度:O(list1.length + list2.length)
空间复杂度:O(list1.length)
扩展问题:两个链表都不存在环,如果相交,给出相交的第一个点。
比较好的方法有两个:
一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。O(list2.length)
    >--- 大于号是两个链表,'-- '是相交重合部分。
 
二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。
这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。
 
阅读(5919) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~