单链表的环检测算法

来源:互联网 发布:协同过滤推荐算法 java 编辑:程序博客网 时间:2024/06/05 03:43

主要参考 http://ostermiller.org/find_loop_singly_linked_list.html

该文中先列举了多种错误的做法,在最后给出了两种正确的算法之前还给出了一种费劲但勉强正确的诡异算法。

这里就只讨论此两种正确算法吧。

第一种算法是 Floyd 环检测算法,也被形象的称为“龟兔赛跑算法”。因为使用了两个迭代指针,都从表头开始遍历,其中一个每步进一次另一个会步进两次,一快一慢很像乌龟与兔子。

原理:因为如果单链表中存在环的话,这两个迭代指针最终都会跑进环里面并循环绕圈,就像体育场长跑的跑道,维持快慢不变得话,跑得快的选手会绕到后面追上跑慢的选手。

Node* cycle_checking(Node* list){  Node *fast, *slow;  for (fast = slow = list; fast && fast != slow; fast = fast->next, slow = slow->next) {    fast = fast->next;    if (!fast || fast == slow)      break;  }  return fast; // return value is the pointer to the cycle entrance,               // except that NULL meaning no cycle.}

第二种 Brent 算法是置一个哨节点,并将遍历过程中的每一个节点都与哨节点比较,但我们都知道如果选定的哨节点不在环中的哨节点是不起作用的(这也是最常见的以第k个节点作为哨节点的一种错误算法),所以此算法使用一种手段,在经过一个逐次倍增的检测间隔后重新选定步进中的当前被测节点为新的哨节点,也就是说哨节点也会往前移动,最终哨节点肯定会进入环中的。

Node* cycle_checking(Node* list){  Node *current, *check;  int counter = 0, interval = 2;  for(current = check = list; current && current != check; current = current->next) {    if (++counter == interval) {      check = current;      interval *= 2;      counter = 0;    }  }  return current; // return value is the pointer to the cycle entrance,                  // except NULL meaning no cycle.}
仔细体味发现两种算法其实有一些相似的地方,都有两个指针,而且用两者的比较结果作为环存在的证据。将 slow 当成是一个哨节点的话,则其意义与 check 相仿。第一种算法的两个指针都老老实实的在往前跑,slow 落后于 fast。第二种算法虽然只有 current 在跑,但 check 间或“分享”一下 current 的进度,可看成时跑时停,也是 check 落后于 current。在环中最后都是以领先者反过来追上落后者而结束。

0 0
原创粉丝点击