链表面试题之有环链表问题

链表面试题之有环链表问题

作者: cnyao 来源: 博客园 发布时间: 2009-11-26 21:41 阅读: 1291 次 原文链接 全屏阅读  [收藏]

链表在面试中出现的频率很高,有的比较正常,考链表的常规操作,主要看基本功是否扎实,有些就比较难,难在思维的改变和是否能够想到对应的点。这里出现的是其中一个题目,我称之为有环链表问题。也就是从判断一个单链表是否存在循环而扩展衍生的问题。下面来看问题如何解决。

首先来看最基本的这个问题:如何判断一个单链表是否存在循环,链表数目未知。算法不能破坏链表。

这里我们可以想到有三种解决的方法。

第一种方法,将所有的遍历过的节点用某个结构存储起来,然后每遍历一个节点,都在这个结构中查找是否遍历过,如果找到有重复,则说明该链表存在循环;如果直到遍历结束,则说明链表不存在循环。

这个结构我们可以使用hash来做,hash中存储的值为节点的内存地址,这样查找的操作所需时间为O(1),遍历操作需要O(n),hash表的存储空间需要额外的O(n)。所以整个算法的时间复杂度为O(n),空间复杂度为O(n)。

第二种方法,比较的特别,是使用反转指针的方法,每过一个节点就把该节点的指针反向。

当有环的时候,最后指针会定位到链表的头部,如果到最后,都没有再到头部,那说明链表不存在循环。

这个方法会破坏掉链表,所以如果要求是不能破坏链表的话,我们最后就还需要反转一下,再将链表恢复。

这个方法使用的空间复杂度为O(1),其实是使用了3个指针,用于进行反转。同时,时间复杂度为O(n)。

第三种方法,可能大家已经知道了,同时也是面试官大多想要得到的答案,就是快慢指针。

快指针pf(f就是fast的缩写)每次移动2个节点,慢指针ps(s为slow的缩写)每次移动1个节点,如果快指针能够追上慢指针,那就说明其中有一个环,否则不存在环。

这个方法的时间复杂度为O(n),空间复杂度为O(1),实际使用两个指针。

【扩展】

对于这个问题,还有一些扩展性的问题。比如:如何找到环的开始节点也就是环首,如何得到环的长度,如何解开环等等,其中的关键就是如何找到其中对应的规律。

当快慢指针都进入环之后,可以观察到快指针每次都接近慢指针一个节点位置。这样,当两个指针相遇的时候,其从慢指针进入环开始走过的路程肯定是小于环的长度x的。而假设链表的第一个节点到环的开始节点的长度为y。

画图可以得知:

 

 

 

 

 

 

 

 

 

 

 

 

 

根据图上面的示意,快指针pf需要追z个节点,就可以追上慢指针ps,而一个iterationpf会追上ps一个节点,所以需要用ziteration

pspf相遇了,可以肯定的是这两个指针肯定是在环上相遇的。

此时,还是继续一快一慢,根据上面得到的规律,经过环长x,这两个指针第二次相遇。这样,我们可以得到环中一共有多少个节点。

 

 

 

 

 

 

 

 

 

 

 

 

假设相遇点为Q点,Q点到m点的距离为r,第一次相遇的时候,ps走的距离为y+z,而pf走的距离为2y+2z,要求环的开始节点位置,也就是求得y的值即可。

 

 

 

 

 

 

 

 

 

 

 

 

 

当重合的时候,pf走了2*all步,ps走了all步,此时两个指针都以步长为1进行回退,那ps就回退到第一个节点W,而pf回退到ps的位置。ps的位置就是Q点,此时从W点或Q点出发,x步之后,都会落到黄色的点Q点上。那因为此时使用的两个指针都是步长为1的,那第一个重合的点是哪个呢?

可以看出是环首的红色点m。而使用的步长就是y

 

所以总结如下:

使用快慢指针,第一次相遇,表明存在循环。

继续快慢指针,第二次相遇,得到的iteration步长为环的长度。

分别从相遇点和第一个节点出发,都是步长为1的指针,当相遇时,得到的iteration步长为环首的位置。

这个题目的特殊情况就是指针指向的就是第一个节点,那得到的就是一个循环链表。

附上扩展题的代码


1 bool hasLoop(linkedlist llist)
2 {
3 listnode *pf=llist;
4 listnode *ps=llist;
5
6 while(true)
7 {
8 if(pf && pf->next)
9 {
10 pf=pf->next->next;
11 }
12 else
13 return false;
14
15 ps=ps->next;
16
17 if(ps==pf)
18 {
19 //yes, there is a loop.
20 //if you just need to check if there is a loop, just return.
21 //but we need to extend it. So break to calc more.
22 break;
23 }
24 }
25
26 //calc the length of the circle.
27 int lengthX=0;
28 do{
29 ps=ps->next;
30 pf=pf->next->next;
31 lengthX++;
32 }while(ps!=pf)
33
34 //find the position of circle.
35 int lengthY=0;
36 linkedlistnode *p=llist;
37 while(p!=ps)
38 {
39 p=p->next;
40 ps=ps->next;
41 lengthY++;
42 }
43
44 printf("the length of circle is %d, the position of first elem in circle is %d./n"lengthX, lengthY);
45
46 return true;
47

使用到的参考,多谢这些兄弟,不然还要多想好久:

1. http://www.cnblogs.com/shawn-zhou/archive/2008/11/26/1341307.html

2. http://bbs.chinaunix.net/viewthread.php?tid=896018

这段时间会开始陆续写面试题的解决,希望大家多多提供题目,多多指教,多多讨论,谢谢~

:)

PS:用chrome来发表文章很快,但是图片和文章搭配的时候,似乎排版就有点小问题,不知道大家有没有遇到过?

再PS:图画得不好,大家见谅哈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值