弗洛伊德循环寻找算法
Floyd 的循环查找算法或 Hare-Tortoise 算法是一种指针算法,它只使用两个指针,以不同的速度在序列中移动。该算法用于在链表中查找循环。它使用两个指针,其中一个的移动速度是另一个的两倍。较快的一个称为较快指针,另一个称为慢速指针。
Floyd 的循环查找算法如何工作?
在遍历链表时,会发生其中一件事情——
- Fast 指针可能到达末尾(NULL),这表明链表中没有循环。
- 快速指针在某个时间再次捕获慢速指针,因此链表中存在循环。
例子:
伪代码:
- 初始化两指针并开始遍历链表。
- 将慢速指针移动一个位置。
- 将快速指针移动两个位置。
- 如果两个指针在某个点相遇,则存在循环,如果快速指针与结束位置相遇,则不存在循环。
下面是实现上述方法的 C++ 程序:
C++
// C++ program to implement
// the above approach
#include
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int data)
{
this->data = data;
next = NULL;
}
};
// initialize a new head for the linked list
Node* head = NULL;
class Linkedlist {
public:
// insert new value at the start
void insert(int value)
{
Node* newNode = new Node(value);
if (head == NULL)
head = newNode;
else {
newNode->next = head;
head = newNode;
}
}
// detect if there is a loop
// in the linked list
bool detectLoop()
{
Node *slowPointer = head,
*fastPointer = head;
while (slowPointer != NULL
&& fastPointer != NULL
&& fastPointer->next != NULL) {
slowPointer = slowPointer->next;
fastPointer = fastPointer->next->next;
if (slowPointer == fastPointer)
return 1;
}
return 0;
}
};
int main()
{
Linkedlist l1;
// inserting new values
l1.insert(10);
l1.insert(20);
l1.insert(30);
l1.insert(40);
l1.insert(50);
// adding a loop for the sake
// of this example
Node* temp = head;
while (temp->next != NULL)
temp = temp->next;
temp->next = head;
// loop added;
if (l1.detectLoop())
cout << "Loop exists in the"
<< " Linked List" << endl;
else
cout << "Loop does not exists "
<< "in the Linked List" << endl;
return 0;
}
Java
// Java program to implement
// the above approach
import java.util.*;
class GFG{
static class Node {
int data;
Node next;
Node(int data)
{
this.data = data;
next = null;
}
};
// initialize a new head for the linked list
static Node head = null;
static class Linkedlist {
// insert new value at the start
void insert(int value)
{
Node newNode = new Node(value);
if (head == null)
head = newNode;
else {
newNode.next = head;
head = newNode;
}
}
// detect if there is a loop
// in the linked list
boolean detectLoop()
{
Node slowPointer = head,
fastPointer = head;
while (slowPointer != null
&& fastPointer != null
&& fastPointer.next != null) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next.next;
if (slowPointer == fastPointer)
return true;
}
return false;
}
}
public static void main(String[] args)
{
Linkedlist l1 = new Linkedlist();
// inserting new values
l1.insert(10);
l1.insert(20);
l1.insert(30);
l1.insert(40);
l1.insert(50);
// adding a loop for the sake
// of this example
Node temp = head;
while (temp.next != null)
temp = temp.next;
temp.next = head;
// loop added;
if (l1.detectLoop())
System.out.print("Loop exists in the"
+ " Linked List" +"\n");
else
System.out.print("Loop does not exists "
+ "in the Linked List" +"\n");
}
}
// This code is contributed by 29AjayKumar
Python3
# Python code for the above approach
class Node:
def __init__(self, d):
self.data = d
self.next = None
# initialize a new head for the linked list
head = None
# detect if there is a loop
# in the linked list
def detectLoop(head):
slowPointer = head
fastPointer = head
while (slowPointer != None
and fastPointer != None
and fastPointer.next != None):
slowPointer = slowPointer.next
fastPointer = fastPointer.next.next
if (slowPointer == fastPointer):
return 1
return 0
# inserting new values
head = Node(10)
head.next = Node(20)
head.next.next = Node(30)
head.next.next.next = Node(40)
head.next.next.next.next = Node(50)
# adding a loop for the sake
# of this example
temp = head
while (temp.next != None):
temp = temp.next
temp.next = head
# loop added;
if (detectLoop(head)):
print("Loop exists in the Linked List")
else:
print("Loop does not exists in the Linked List")
# This code is contributed by Saurabh Jaiswal
C#
// C# program to implement
// the above approach
using System;
using System.Collections.Generic;
public class GFG {
public class Node {
public int data;
public Node next;
public Node(int data) {
this.data = data;
next = null;
}
};
// initialize a new head for the linked list
static Node head = null;
public class Linkedlist
{
// insert new value at the start
public void insert(int value) {
Node newNode = new Node(value);
if (head == null)
head = newNode;
else {
newNode.next = head;
head = newNode;
}
}
// detect if there is a loop
// in the linked list
public bool detectLoop() {
Node slowPointer = head, fastPointer = head;
while (slowPointer != null && fastPointer != null &&
fastPointer.next != null)
{
slowPointer = slowPointer.next;
fastPointer = fastPointer.next.next;
if (slowPointer == fastPointer)
return true;
}
return false;
}
}
public static void Main(String[] args) {
Linkedlist l1 = new Linkedlist();
// inserting new values
l1.insert(10);
l1.insert(20);
l1.insert(30);
l1.insert(40);
l1.insert(50);
// adding a loop for the sake
// of this example
Node temp = head;
while (temp.next != null)
temp = temp.next;
temp.next = head;
// loop added;
if (l1.detectLoop())
Console.Write("Loop exists in the" + " Linked List" + "\n");
else
Console.Write("Loop does not exists " + "in the Linked List" + "\n");
}
}
// This code is contributed by umadevi9616
Javascript
C++
// C++ program to implement
// the above approach
#include
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int data)
{
this->data = data;
next = NULL;
}
};
// intitalize a new head
// for the linked list
Node* head = NULL;
class Linkedlist {
public:
// insert new value at the start
void insert(int value)
{
Node* newNode = new Node(value);
if (head == NULL)
head = newNode;
else {
newNode->next = head;
head = newNode;
}
}
// detect if there is a loop
// in the linked list
Node* detectLoop()
{
Node *slowPointer = head,
*fastPointer = head;
while (slowPointer != NULL
&& fastPointer != NULL
&& fastPointer->next != NULL) {
slowPointer = slowPointer->next;
fastPointer = fastPointer->next->next;
if (slowPointer == fastPointer)
break;
}
// if no loop exists
if (slowPointer != fastPointer)
return NULL;
// reset slow pointer to head
// and traverse again
slowPointer = head;
while (slowPointer != fastPointer) {
slowPointer = slowPointer->next;
fastPointer = fastPointer->next;
}
return slowPointer;
}
};
int main()
{
Linkedlist l1;
// inserting new values
l1.insert(10);
l1.insert(20);
l1.insert(30);
l1.insert(40);
l1.insert(50);
// adding a loop for the sake
// of this example
Node* temp = head;
while (temp->next != NULL)
temp = temp->next;
// loop added;
temp->next = head;
Node* loopStart = l1.detectLoop();
if (loopStart == NULL)
cout << "Loop does not exists" << endl;
else {
cout << "Loop does exists and starts from "
<< loopStart->data << endl;
}
return 0;
}
Loop exists in the Linked List
时间复杂度: O(n),因为循环遍历一次。
辅助空间: O(1),仅使用两个指针,因此空间复杂度恒定。
为什么弗洛伊德算法有效?
让我们考虑一个例子:
- 让,
X = Distance between the head(starting) to the loop starting point.
Y = Distance between the loop starting point and the first meeting point of both the pointers.
C = The distance of the loop
- 所以在两个指针相遇之前——
The slow pointer has traveled X + Y + s * C distance, where s is any positive constant number.
The fast pointer has traveled X + Y + f * C distance, where f is any positive constant number.
- 由于快速指针的移动速度是慢速指针的两倍,我们可以说快速指针移动的距离是慢速指针移动距离的两倍。所以-
X + Y + f * C = 2 * (X + Y + s * C)
X + Y = f * C – 2 * s * C
We can say that,
f * C – 2 * s * C = (some integer) * C
= K * C
Thus,
X + Y = K * C – ( 1 )
X = K * C – Y – ( 2 )
Where K is some positive constant.
- 现在,如果将慢速指针重新设置到头部(起始位置)并一次移动快慢指针一个单位,可以从第 1 和第 2 等式观察到,它们都会在开始移动 X 距离后相遇循环,因为在重置慢速指针并将其移动 X 距离后,同时从循环汇合点,快速指针也将行进 K * C - Y 距离(因为它已经行进了 Y 距离)。
- 从等式 (2) 可以说 X = K * C - Y 因此,两个指针都将行进距离 X,即在某个点在粉红色节点之后的相同距离,以在循环的起点相遇。
- 在这里,到某个时间点,这意味着快速指针可以完成它已经覆盖了 Y 距离的 K * C 距离。
下面是实现上述方法的 C++ 程序——
C++
// C++ program to implement
// the above approach
#include
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int data)
{
this->data = data;
next = NULL;
}
};
// intitalize a new head
// for the linked list
Node* head = NULL;
class Linkedlist {
public:
// insert new value at the start
void insert(int value)
{
Node* newNode = new Node(value);
if (head == NULL)
head = newNode;
else {
newNode->next = head;
head = newNode;
}
}
// detect if there is a loop
// in the linked list
Node* detectLoop()
{
Node *slowPointer = head,
*fastPointer = head;
while (slowPointer != NULL
&& fastPointer != NULL
&& fastPointer->next != NULL) {
slowPointer = slowPointer->next;
fastPointer = fastPointer->next->next;
if (slowPointer == fastPointer)
break;
}
// if no loop exists
if (slowPointer != fastPointer)
return NULL;
// reset slow pointer to head
// and traverse again
slowPointer = head;
while (slowPointer != fastPointer) {
slowPointer = slowPointer->next;
fastPointer = fastPointer->next;
}
return slowPointer;
}
};
int main()
{
Linkedlist l1;
// inserting new values
l1.insert(10);
l1.insert(20);
l1.insert(30);
l1.insert(40);
l1.insert(50);
// adding a loop for the sake
// of this example
Node* temp = head;
while (temp->next != NULL)
temp = temp->next;
// loop added;
temp->next = head;
Node* loopStart = l1.detectLoop();
if (loopStart == NULL)
cout << "Loop does not exists" << endl;
else {
cout << "Loop does exists and starts from "
<< loopStart->data << endl;
}
return 0;
}
Loop does exists and starts from 50
时间复杂度: O(n),因为我们已经遍历循环一次,然后行进 X 距离。
辅助空间: O(1),因为只使用指针,因此空间复杂度恒定。