C语言 百分网手机站

C语言单向链表环测试并返回环起始节点的方法

时间:2020-11-03 13:12:57 C语言 我要投稿

C语言单向链表环测试并返回环起始节点的方法

  有时候我们需要测试一个单向链表是否存在环。最土鳖的方法就是改变链表的数据结构,给每个节点添加一个bool变量,在未测试时全部初始化为false,然后遍历链表,每访问一个节点,先测试其bool成员来确定这个节点是否被访问过,如果为true,则表示访问过,则有环,否则设置bool成员为true,表明访问过,然后继续测试。下面,小编为大家搜索整理了C语言单向链表环测试并返回环起始节点的方法,希望能给大家带来帮助!更多精彩内容请及时关注我们应届毕业生考试网!

  如果不改变数据结构的话,我们有以下的解决方案:

  1. 测试是否有环:

  我们可以构建两个迭代器来遍历链表,一个每一次移动一个节点,另外一个每次移动两个节点。如果这两个一快一慢的土鳖迭代器相遇了,也就是说他们在某个时刻都到了同一个节点,那么我们可以肯定有环存在。直观的理解就是让两个土鳖一快一慢在400米环形跑道上各选一个位置,然后同时顺时针做死了跑,那么这两个土鳖总能相遇,因为一个比另外一个快。

  如果需要严谨的证明,我们可以这样理解。假设在某个迭代时刻,两个土鳖迭代器(以后简称土鳖)都进入了环,一个距环起始点为i,一个距环起始点为j。这个假设必然有成立的时候,因为跑着跑着他们总会进入环,而且一旦进入那就出不来了,只能做死了跑。然后假设又跑了一会儿,这两个土鳖相遇了,一个土鳖跑了x步,一个跑了2x步。如果这个环总共长n,也就是说慢土鳖需要跑n步才能跑完一圈。然后我们可以得出i+x和j+2x对于n同余,也就是说i+x和j+2x除以n的余数是相同的,写成同余等式就是(i+x)=j+2x(mod n) ,根据同余加减法性质,我们可以让上面的式子减去x=x(mod m),得到i=(j+x)(mod m)。因为x未知,所以上面的式子是个同余方程,i、j都是普通整数,很明显这个方程是有解的。例如2=(1+x)(mod 5)的一个简单解就是1。所以这两个土鳖跑着跑着总会相遇。也就是说我们上面检测环的算法可行,不会死循环。

  2. 获取环起始点:

  基于问题1的分析,快土鳖和慢土鳖总会在某个节点相遇,假设这个点为cross。同事假设环起始点为start。一个显然的事实是,当两个土鳖相遇时,慢土鳖跑过的路径是快土鳖的一半。这样的话,在相遇前,当慢土鳖跑了一般的时候,快土鳖已经经过了相遇点(落脚或者跨越)。这样的话当慢土鳖跑完后半段的时候,快土鳖从相遇点开始又跑了同样的'路程到达了相遇点,这个路程的长度等于慢土鳖总共跑的长度。现在牛逼的地方来了,如果慢土鳖从头开始跑的时候,有另外一个慢土鳖从相遇点cross开始跑,那么他们两个也会在相遇点相遇,我们称这两个土鳖分别为A和B。土鳖B走的路程和快土鳖后半段时间走过的路程是完全一样的,唯一的区别就是他慢一点而已。现在第二个牛逼的地方来了,因为慢土鳖A和B的速度是一样的,那么他们在相遇点之前的节奏也是一样的,也就是说他们在相遇点值钱已经相遇了,而且一同样的速度相伴走到了相遇点cross。他们从什么时候相遇开始这段快乐的旅程呢,当然是环起始点start。我们可以让慢土鳖A和B从相遇点倒退,这样就能理解为什么他们在start点相遇了。OK,现在我们有了解决方案,让慢土鳖A从链表头start开始跑,让另外一个慢土鳖从相遇点cross开始跑,他们第一次的相遇点就是环起始点。

  大功告成,标点符号(废话)有点多,大家不要介意。

  下面是C++代码:

  1 #include

  2 #include

  3

  4 template

  5 struct Node

  6 {

  7 T value;

  8 Node* next;

  9 };

  10

  11 //Test if a linked list has circle

  12 template

  13 bool hasLoop(Node* linkedList, Node** loopCross = NULL)

  14 {

  15 //empty linked list, no circle

  16 if(linkedList == NULL || loopCross == NULL) return false;

  17

  18 Node* slowWalker = linkedList;

  19 Node* quickWalker = linkedList;

  20 while(quickWalker != NULL && quickWalker->next != NULL)

  21 {

  22 // move the walker

  23 slowWalker = slowWalker->next; //one each step

  24 quickWalker = quickWalker->next->next; //two each step

  25 if(slowWalker == quickWalker)

  26 {

  27 //has circle

  28 *loopCross = slowWalker;

  29 return true;

  30 }

  31 }

  32

  33 return false;

  34 }

  35

  36 //Get the loop start node

  37 template

  38 Node* getLoopStart(Node* linkedList, Node* loopCross)

  39 {

  40 Node* startFromHead = linkedList;

  41 Node* startFromCross = loopCross;

  42 // Move one pointer from head and move another from the cross node.

  43 // They will meet each other at the loop start node.

  44 while(startFromHead != startFromCross)

  45 {

  46 startFromHead = startFromHead->next;

  47 startFromCross = startFromCross->next;

  48 }

  49 return startFromHead;

  50 }

  51

  52 int main()

  53 {

  54 Node* linkedList = new Node();

  55 linkedList->value = 0;

  56 linkedList->next = NULL;

  57

  58 Node* pNode = linkedList;

  59 Node* crossNode = NULL;

  60

  61 for(int i = 1; i < 100; i++)

  62 {

  63 Node* tem = new Node();

  64 tem->value = i;

  65 tem->next = NULL;

  66

  67 pNode->next = tem;

  68 pNode = tem;

  69 // set the cross node;

  70 if(i == 66)

  71 crossNode = tem;

  72 }

  73

  74 printf("test normal linked list:\n");

  75 if(hasLoop(linkedList))

  76 printf("has circle.\n");

  77 else

  78 printf("no circle.\n");

  79

  80 printf("test circle linked list:\n");

  81 pNode->next = crossNode; // Create a circle

  82

  83 Node* loopCross = NULL;

  84 if(hasLoop(linkedList, &loopCross))

  85 {

  86 printf("has circle.\n");

  87 Node* loopStart = getLoopStart(linkedList, loopCross);

  88 if(loopStart != NULL)

  89 printf("the value of the circle start node is %d\n", loopStart->value);

  90 }

  91 else

  92 printf("no circle.");

  93 }

【C语言单向链表环测试并返回环起始节点的方法】相关文章:

1.C语言的循环链表和约瑟夫环

2.c语言链表的用法

3.链表的C语言实现方法编程学习

4.c语言链表的用法有哪些

5.C语言练习测试试题

6.C语言输出菱形的方法

7.C语言的reduce方法应用

8.C语言的冒泡排序方法

9.C语言高效编程的方法