免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: emacsnw
打印 上一主题 下一主题

一个关于单向链表的面试题。 [复制链接]

论坛徽章:
0
81 [报告]
发表于 2007-04-28 22:16 |只看该作者
把链表看成一个有向图,深度优先遍历该有向图,判断有无循环出现。
懒得再用中文写一遍具体算法了,看下面的代码实现吧,英文注释解释的很清楚了。

时间复杂度 O(e), 链表边的总数。
空间复杂度 O(1).
有向图采用邻接表实现。

  1. /* file: DFSDetectLoop.cpp */
  2. /*
  3. *  Detect if the graph has loop -- For both Undigraph and digraph
  4. *  Complexity: O(e); e is the number of arcs in Graph.
  5. *   
  6. *  BUG Reported:
  7. *  1. Apr-26-07
  8. *     Not support Undigraph yet ! Fix me !!!
  9. *     - Fixed on Apr-26-08.
  10. *  
  11. *  Return
  12. *  1 - Loop detected.
  13. *  0 - No loop detected.     
  14. *  *
  15. *  Algrithm:
  16. *  1. Init all the nodes color to WHITE.
  17. *  2. DFS graph
  18. *      For each the nodes v in graph, do step (1) and (2).
  19. *      (1) If v is WHITE, DFS from node v:
  20. *          (a) Mark v as GRAY.   
  21. *          (b) For every nodes tv adjacent with node v,
  22. *              (i)   If the current visiting node is gray, then loop detected. exit.
  23. *              (ii)  Goto Step (1).
  24. *              (iii) All the nodes on sub-tree of tv have been visited. Mark node tv as BLACK.
  25. *       (2) All the nodes on sub-tree of v have been visited. Mark node v as BLACK.
  26. *      
  27. * Function DFSDetectLoop is valid for both Undigraph and digraph.
  28. *
  29. * */
  30. int DFSDetectLoop (ALGraph *graph, int VisitFunc (ALGraph *graph, int v))
  31. {
  32.     int v;

  33.     for (v = 0; v < graph->vexnum; v++)
  34.     {   
  35.         MarkNodeColor (graph, v, WHITE);
  36.     }
  37.     for (v = 0; v < graph->vexnum; v++)
  38.     {   
  39.         if (graph->vertices[v].color == WHITE)
  40.         {   
  41.             /* We are good to call DFSDetectLoopSub the first
  42.              * time with pv = -1, because no node equals -1.
  43.              * */
  44.             if (1 == DFSDetectLoopSub (graph, v, -1, VisitFunc))
  45.                 return 1;
  46.         }
  47.         MarkNodeColor (graph, v, BLACK);
  48.     }
  49.     return 1;
  50. }

  51. /*         
  52. * Start from node v, DFS graph to detect loop.
  53. *  pv is the node that just visited v. pv is used to avoid v to visit pv again.
  54. *  pv is introduced to support Undigraph.
  55. *  
  56. * NOTE:
  57. *      Before calling DFSDetectLoopSub, make sure node v is not visited yet.
  58. * */
  59. int DFSDetectLoopSub (ALGraph *graph, int v, int pv, int VisitFunc (ALGraph *graph, int v))
  60. {
  61.    assert (graph->vertices[v].color == WHITE);

  62.    MarkNodeColor (graph, v, GRAY);

  63.    VisitFunc (graph, v);

  64.    ArcNode *arc;
  65.    arc = graph->vertices[v].firstarc;
  66.    while (arc)
  67.    {
  68.         int tv = arc->adjvex;

  69.         /* For Undigraph, if tv equals pv, this arc should not be count.
  70.          * Because we have just visited from pv to v.
  71.          * Just go ahead to check next vertex connected with v.
  72.          * 1----2, after visit 1, we will visit 2, while visiting 2, 1 will be the 1st node visited.
  73.          *
  74.          * For digraph, we need to check loop even tv equals pv.
  75.          * Because there is case that node v points to u, and u points to v.
  76.          * */
  77.         if ((graph->kind == AG) && (tv != pv))
  78.         {
  79.             if ( graph->vertices[tv].color == GRAY )
  80.             {
  81.                 cout << "Gray node visited at node: " << tv + 1 <<endl;
  82.                 cout << "DFSDetectLoopSub: Loop Detected at from node " << v + 1<<" to "<< tv + 1 <<" !" <<endl;
  83.                 return 1;
  84.             }

  85.             if (graph->vertices[tv].color == WHITE)
  86.             {
  87.                 if (1 == DFSDetectLoopSub (graph, tv, v, VisitFunc))
  88.                 {
  89.                     return 1;
  90.                 }
  91.             }
  92.             /* At this line:
  93.              * (1)If tv's color is already BLACK; Go ahead checking next arc;
  94.              * (2)If the sub-tree of node tv has all been visited, mark as BLACK and check next arc;
  95.              * Backward tv to to v's other adjacent node. So tv should be marked as black.
  96.              * */
  97.             MarkNodeColor (graph, tv, BLACK);
  98.         }

  99.         arc = arc->nextarc;
  100.    }
  101.    return 0;
  102. }

复制代码

论坛徽章:
0
82 [报告]
发表于 2007-04-29 18:50 |只看该作者
80楼的方法肯定是错的
a+y*b和a+p*b理论上可以相等
但是不要忘了,这个时候你的两个指针步长都是1,速度相同,永远都追不上的

论坛徽章:
0
83 [报告]
发表于 2007-04-29 20:55 |只看该作者
原帖由 deadlylight 于 2007-4-29 18:50 发表
80楼的方法肯定是错的
a+y*b和a+p*b理论上可以相等
但是不要忘了,这个时候你的两个指针步长都是1,速度相同,永远都追不上的


多谢指点
想了一下,的确是这里条件不够,有可能出现出现始终两个指针差n*b(n>0)圈的情况(并没有去找一个特殊的例子,但是我想了一下,这个情况应该是可能出现的),这样就会产生永远追不上的情况,那是否可以设置一个条件,记录下第一次相遇的那个节点,如果指针1又一次到达这个节点的时候,指针1暂时停止前进,而指针2向前b个节点呢?

论坛徽章:
0
84 [报告]
发表于 2007-04-30 09:51 |只看该作者
这个帖子这么长了。看了后面的回复,觉得很多朋友没有理解 Emacsnw 的方法。其实他的方法已经很简明了,相比之下我给出的解答则比较艰涩隐晦。现在把我的方法解释一次,顺带也把 emacsnw 的方法说一下。


1、我的想法



设这些点的下标为 0, 1, 2, ...


看上面的图,红点是大步-小步法找到的。设起点到红点的长度为 i,环的长度为 b,则

从红点出发,每 b 步后,都回到红点。如果我们让指针从红点出发,回退 b 步,得到的点标记为黄色。此时,从红点或黄点出发,b 步后都落在红点上。容易看出,用两个指针,分别从红,黄两点出发,步幅为 1,则它们的首个重合点就是环首。

回退 b 步得到的点在实现上可以通过从起点走 i-b 步得到。 由于 2i>=i+b, i-b 总是合法的。

我在前面帖子给出的方法与这个是一样的,只是陈述方式不同,而且前面的帖子符号有点混淆。

另外 i=0 (mod b) 总是成立的,因为 i = 2i (mod b)。 所以我们有  b|i


2、对 Emacsnw 的方法的解释
用两个指针一个指向第 i 点,另一个指向第 2i 点,而它们都指向红点。现在它们都回退 i 步(步幅 1),则一个指针退到起点0,另一个指针退到 i 点。 此时让两个指针按步幅1 向前走直到相交就是环首。

设置退到 0 点的指针叫 p,停留在 i 点的叫 q, 则 q 每走 b 步都留在红点,所以我们可以把 p 的起点提前 b 的一个倍数而不影响结果。这就是我在前面帖子中的做法,用 i 去除以 b.

[ 本帖最后由 win_hate 于 2007-4-30 10:12 编辑 ]

论坛徽章:
0
85 [报告]
发表于 2007-04-30 10:16 |只看该作者
你这个图画的还有才了,呵呵。

原帖由 win_hate 于 2007-4-30 09:51 发表
这个帖子这么长了。看了后面的回复,觉得很多朋友没有理解 Emacsnw 的方法。其实他的方法已经很简明了,相比之下我给出的解答则比较艰涩隐晦。现在把我的方法解释一次,顺带也把 emacsnw 的方法说一下。


1、 ...

论坛徽章:
0
86 [报告]
发表于 2007-04-30 12:52 |只看该作者
win_hate,Emacsnw 的连个方法我觉得是一样的。应该是最好的解法了.时间是O(n),空间(1)

论坛徽章:
0
87 [报告]
发表于 2007-04-30 15:40 |只看该作者
原帖由 win_hate 于 2007-4-30 09:51 发表
这个帖子这么长了。看了后面的回复,觉得很多朋友没有理解 Emacsnw 的方法。其实他的方法已经很简明了,相比之下我给出的解答则比较艰涩隐晦。现在把我的方法解释一次,顺带也把 emacsnw 的方法说一下。


1、 ...



学习了,是我没有理解清楚。


GKL 该用户已被删除
88 [报告]
发表于 2007-05-01 12:20 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
GKL 该用户已被删除
89 [报告]
发表于 2007-05-01 12:22 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
GKL 该用户已被删除
90 [报告]
发表于 2007-05-01 12:57 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP