弗洛伊德的兔子与乌龟

算法解读

Floyd判圈算法(Floyd Cycle Detection Algorithm)，又称龟兔赛跑算法(Tortoise and Hare Algorithm)，是一个可以在有限状态机、迭代函数或者链表上判断是否存在环，求出该环的起点与长度的算法。该算法据高德纳称由美国科学家罗伯特·弗洛伊德发明。

• 形象化解释

• 判断是否有环

• 环的长度

• 环的起点

代码

• 伪代码
t := &S
h := &S                 //令指针t和h均指向起始节点S。
repeat
t := t->next
h := h->next
if h is not NULL       //要注意这一判断一般不能省略
h := h->next
until t = h or h = NULL
if h != NULL             //如果存在环的話
n := 0
repeat                //求环的度
t := t->next
n := n+1
until t = h
t := &S               //求环的一个起点
while t != h
t := t->next
h := h->next
P := *t


• c++代码
#include <bits/stdc++.h>

struct Node//链表节点
{
int data;
Node * next;
};

bool findCircle(Node * list){
Node* p1  = list->next, *p2;//p1移动1个单位
bool flag;
if(p1 == NULL){//链表只有1个节点
return false;
}
p2 = p1 -> next;//p2移动两个单位
while(p1 != p2 && p2 != NULL){
p1 = p1->next;
p2 = p2->next;
if(p2 == NULL){
break;
}
p2 = p2->next;//p2移动两个单位
}
if(p2 == NULL){
return false;
}
else{
//存在环，首先求环的长度
int cnt = 1, p2 = p2->next;
while(p2 != p1){
++cnt;
p2 = p2->next;
}
//此时cnt就是环的长度

//求环的起点
p1 = list;
while(p1 != p2){
p1 = p1->next;
p2 = p2->next;
}
//此时p1与p2都指向环的起点
return true;
}
}


• 空间复杂度

• 时间复杂度

Brent的移动的兔子和传送的乌龟

简介

How do you determine if your singly-linked list has a cycle? In 1980, Brent invented an algorithm that not only worked in linear time, but required less stepping than Floyd’s Tortoise and the Hare algorithm (however it is slightly more complex). Although stepping through a ‘regular’ linked list is computationally easy, these algorithms are also used for factorization and pseudorandom number generators, linked lists are implicit and finding the next member is computationally difficult.

1980年，Brent提出了一种算法，不仅能在线性时间内找到环，并且使用的步数比Floyd的判圈算法要少。

算法思路

Brent’s algorithm features a moving rabbit and a stationary, then teleporting, turtle. Both turtle and rabbit start at the top of the list. The rabbit takes one step per iteration. If it is then at the same position as the stationary turtle, there is obviously a loop. If it reaches the end of the list, there is no loop.

Of course, this by itself will take infinite time if there is a loop. So every once in a while, we teleport the turtle to the rabbit’s position, and let the rabbit continue moving. We start out waiting just 2 steps before teleportation, and we double that each time we move the turtle.

算法效率

Note that like Floyd’s Tortoise and Hare algorithm, this one runs in O(N). However you’re doing less stepping than with Floyd’s (in fact the upper bound for steps is the number you would do with Floyd’s algorithm). According to Brent’s research, his algorithm is 24-36% faster on average for implicit linked list algorithms.

代码

• 伪代码
turtle = top
rabbit = top

steps_taken = 0
step_limit = 2

forever:
if rabbit == end:
return 'No Loop Found'
rabbit = rabbit.next

steps_taken += 1

if rabbit == turtle:
return 'Loop found'

if steps_taken == step_limit:
steps_taken = 0
step_limit *= 2
// teleport the turtle
turtle = rabbit


• c++代码
#include <bits/stdc++.h>

struct Node//链表节点
{
int data;
Node * next;
};

bool findCircle(Node * list){
Node* p1  = list->next, *p2 = list;//p1移动1个单位
int steps_taken = 1, step_limit = 2;
bool flag;
if(p1 == NULL){//链表只有1个节点
return false;
}

while(p1 != p2 && p1 != NULL){
if(steps_taken == step_limit){//达到传送时间
steps_taken = 0;//步数清0
step_limit = step_limit * 2;//传送时间翻倍
p2 = p1;
}
p1 = p1->next;
++steps_taken;
if(p1 == NULL){
break;
}
}
if(p1 == NULL){
return false;
}
else{
//存在环，首先求环的长度
int cnt = 1, p2 = p2->next;
while(p2 != p1){
++cnt;
p2 = p2->next;
}
//此时cnt就是环的长度
return true;
}
}


©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客