📜  用于检测链表中的循环的 C++ 程序

📅  最后修改于: 2022-05-13 01:55:05.655000             🧑  作者: Mango

用于检测链表中的循环的 C++ 程序

给定一个链表,检查链表是否有循环。下图显示了一个带有循环的链表。

以下是执行此操作的不同方法。

解决方案 1:哈希方法:

逐一遍历列表,并不断将节点地址放入哈希表中。在任何时候,如果达到 NULL,则返回 false,如果当前节点的下一个指向 Hash 中先前存储的任何节点,则返回 true。

C++
// C++ program to detect loop in a linked list
#include 
using namespace std;
  
/* Link list node */
struct Node {
    int data;
    struct Node* next;
};
  
void push(struct Node** head_ref, int new_data)
{
    /* allocate node */
    struct Node* new_node = new Node;
  
    /* put in the data  */
    new_node->data = new_data;
  
    /* link the old list off the new node */
    new_node->next = (*head_ref);
  
    /* move the head to point to the new node */
    (*head_ref) = new_node;
}
  
// Returns true if there is a loop in linked list
// else returns false.
bool detectLoop(struct Node* h)
{
    unordered_set s;
    while (h != NULL) {
        // If this node is already present
        // in hashmap it means there is a cycle
        // (Because you we encountering the
        // node for the second time).
        if (s.find(h) != s.end())
            return true;
  
        // If we are seeing the node for
        // the first time, insert it in hash
        s.insert(h);
  
        h = h->next;
    }
  
    return false;
}
  
/* Driver program to test above function*/
int main()
{
    /* Start with the empty list */
    struct Node* head = NULL;
  
    push(&head, 20);
    push(&head, 4);
    push(&head, 15);
    push(&head, 10);
  
    /* Create a loop for testing */
    head->next->next->next->next = head;
  
    if (detectLoop(head))
        cout << "Loop found";
    else
        cout << "No Loop";
  
    return 0;
}
// This code is contributed by Geetanjali


C++
// C++ program to detect loop in a linked list
#include 
using namespace std;
  
/* Link list node */
struct Node {
    int data;
    struct Node* next;
    int flag;
};
  
void push(struct Node** head_ref, int new_data)
{
    /* allocate node */
    struct Node* new_node = new Node;
  
    /* put in the data */
    new_node->data = new_data;
  
    new_node->flag = 0;
  
    /* link the old list off the new node */
    new_node->next = (*head_ref);
  
    /* move the head to point to the new node */
    (*head_ref) = new_node;
}
  
// Returns true if there is a loop in linked list
// else returns false.
bool detectLoop(struct Node* h)
{
    while (h != NULL) {
        // If this node is already traverse
        // it means there is a cycle
        // (Because you we encountering the
        // node for the second time).
        if (h->flag == 1)
            return true;
  
        // If we are seeing the node for
        // the first time, mark its flag as 1
        h->flag = 1;
  
        h = h->next;
    }
  
    return false;
}
  
/* Driver program to test above function*/
int main()
{
    /* Start with the empty list */
    struct Node* head = NULL;
  
    push(&head, 20);
    push(&head, 4);
    push(&head, 15);
    push(&head, 10);
  
    /* Create a loop for testing */
    head->next->next->next->next = head;
  
    if (detectLoop(head))
        cout << "Loop found";
    else
        cout << "No Loop";
  
    return 0;
}
// This code is contributed by Geetanjali


C++
// C++ program to detect loop in a linked list
#include 
using namespace std;
  
/* Link list node */
class Node {
public:
    int data;
    Node* next;
};
  
void push(Node** head_ref, int new_data)
{
    /* allocate node */
    Node* new_node = new Node();
  
    /* put in the data */
    new_node->data = new_data;
  
    /* link the old list off the new node */
    new_node->next = (*head_ref);
  
    /* move the head to point to the new node */
    (*head_ref) = new_node;
}
  
int detectLoop(Node* list)
{
    Node *slow_p = list, *fast_p = list;
  
    while (slow_p && fast_p && fast_p->next) {
        slow_p = slow_p->next;
        fast_p = fast_p->next->next;
        if (slow_p == fast_p) {
            return 1;
        }
    }
    return 0;
}
  
/* Driver code*/
int main()
{
    /* Start with the empty list */
    Node* head = NULL;
  
    push(&head, 20);
    push(&head, 4);
    push(&head, 15);
    push(&head, 10);
  
    /* Create a loop for testing */
    head->next->next->next->next = head;
    if (detectLoop(head))
        cout << "Loop found";
    else
        cout << "No Loop";
    return 0;
}
  
// This is code is contributed by rathbhupendra


C++
// C++ program to return first node of loop
#include 
using namespace std;
  
struct Node {
    int key;
    struct Node* next;
};
  
Node* newNode(int key)
{
    Node* temp = new Node;
    temp->key = key;
    temp->next = NULL;
    return temp;
}
  
// A utility function to print a linked list
void printList(Node* head)
{
    while (head != NULL) {
        cout << head->key << " ";
        head = head->next;
    }
    cout << endl;
}
  
// Function to detect first node of loop
// in a linked list that may contain loop
bool detectLoop(Node* head)
{
  
    // Create a temporary node
    Node* temp = new Node;
    while (head != NULL) {
  
        // This condition is for the case
        // when there is no loop
        if (head->next == NULL) {
            return false;
        }
  
        // Check if next is already
        // pointing to temp
        if (head->next == temp) {
            return true;
        }
  
        // Store the pointer to the next node
        // in order to get to it in the next step
        Node* nex = head->next;
  
        // Make next point to temp
        head->next = temp;
  
        // Get to the next node in the list
        head = nex;
    }
  
    return false;
}
  
/* Driver program to test above function*/
int main()
{
    Node* head = newNode(1);
    head->next = newNode(2);
    head->next->next = newNode(3);
    head->next->next->next = newNode(4);
    head->next->next->next->next = newNode(5);
  
    /* Create a loop for testing(5 is pointing to 3) */
    head->next->next->next->next->next = head->next->next;
  
    bool found = detectLoop(head);
    if (found)
        cout << "Loop Found";
    else
        cout << "No Loop";
  
    return 0;
}


C++
// C++ program to return first node of loop
#include 
using namespace std;
  
struct Node {
    int key;
    struct Node* next;
};
  
Node* newNode(int key)
{
    Node* temp = new Node;
    temp->key = key;
    temp->next = NULL;
    return temp;
}
  
// A utility function to print a linked list
void printList(Node* head)
{
    while (head != NULL) {
        cout << head->key << " ";
        head = head->next;
    }
    cout << endl;
}
  
/*returns distance between first and last node every time
 * last node moves forwards*/
int distance(Node* first, Node* last)
{
    /*counts no of nodes between first and last*/
    int counter = 0;
  
    Node* curr;
    curr = first;
  
    while (curr != last) {
        counter += 1;
        curr = curr->next;
    }
  
    return counter + 1;
}
  
// Function to detect first node of loop
// in a linked list that may contain loop
bool detectLoop(Node* head)
{
  
    // Create a temporary node
    Node* temp = new Node;
  
    Node *first, *last;
  
    /*first always points to head*/
    first = head;
    /*last pointer initially points to head*/
    last = head;
  
    /*current_length stores no of nodes between current
     * position of first and last*/
    int current_length = 0;
  
    /*current_length stores no of nodes between previous
     * position of first and last*/
    int prev_length = -1;
  
    while (current_length > prev_length && last != NULL) {
        // set prev_length to current length then update the
        // current length
          prev_length = current_length;
        // distance is calculated
        current_length = distance(first, last);
        // last node points the next node
        last = last->next;
    }
      
    if (last == NULL) {
        return false;
    }
    else { 
        return true;
    }
}
  
/* Driver program to test above function*/
int main()
{
    Node* head = newNode(1);
    head->next = newNode(2);
    head->next->next = newNode(3);
    head->next->next->next = newNode(4);
    head->next->next->next->next = newNode(5);
  
    /* Create a loop for testing(5 is pointing to 3) */
    head->next->next->next->next->next = head->next->next;
  
    bool found = detectLoop(head);
    if (found)
        cout << "Loop Found";
    else
        cout << "No Loop Found";
  
    return 0;
}


C++
// C++ program to return first node of loop
#include 
using namespace std;
  
struct Node {
    int key;
    struct Node* next;
};
  
Node* newNode(int key)
{
    Node* temp = new Node;
    temp->key = key;
    temp->next = NULL;
    return temp;
}
  
// Function to detect first node of loop
// in a linked list that may contain loop
bool detectLoop(Node* head)
{
      
    // If the head is null we will return false
    if (!head)
        return 0;
    else {
        
        // Traversing the linked list 
        // for detecting loop
        while (head) {
            // If loop found
            if (head->key == -1) {
                return true;
            }
            
            // Changing the data of visited node to any
            // value which is outside th given range here it
            // is supposed the given range is (1 <= Data on
            // Node <= 10^3)
            else {
                head->key = -1;
                head = head->next;
            }
        }
        // If loop not found return false
        return 0;
    }
}
  
/* Driver program to test above function*/
int main()
{
    Node* head = newNode(1);
    head->next = newNode(2);
    head->next->next = newNode(3);
    head->next->next->next = newNode(4);
    head->next->next->next->next = newNode(5);
  
    /* Create a loop for testing(5 is pointing to 3) */
    head->next->next->next->next->next = head->next->next;
  
    bool found = detectLoop(head);
    cout << found << endl;
    return 0;
}


输出
Loop found

复杂性分析:

  • 时间复杂度: O(n)。
    只需要遍历一次循环。
  • 辅助空间: O(n)。
    n 是在 hashmap 中存储值所需的空间。

解决方案2这个问题可以通过修改链表数据结构,在没有hashmap的情况下解决。
方法:这个解决方案需要修改基本的链表数据结构。

  • 每个节点都有一个已访问标志。
  • 遍历链表并不断标记访问过的节点。
  • 如果您再次看到访问过的节点,则存在一个循环。此解决方案在 O(n) 中有效,但需要每个节点的附加信息。
  • 该解决方案的一种不需要修改基本数据结构的变体可以使用哈希来实现,只需将访问节点的地址存储在哈希中,如果您看到哈希中已经存在的地址,则存在一个循环。

C++

// C++ program to detect loop in a linked list
#include 
using namespace std;
  
/* Link list node */
struct Node {
    int data;
    struct Node* next;
    int flag;
};
  
void push(struct Node** head_ref, int new_data)
{
    /* allocate node */
    struct Node* new_node = new Node;
  
    /* put in the data */
    new_node->data = new_data;
  
    new_node->flag = 0;
  
    /* link the old list off the new node */
    new_node->next = (*head_ref);
  
    /* move the head to point to the new node */
    (*head_ref) = new_node;
}
  
// Returns true if there is a loop in linked list
// else returns false.
bool detectLoop(struct Node* h)
{
    while (h != NULL) {
        // If this node is already traverse
        // it means there is a cycle
        // (Because you we encountering the
        // node for the second time).
        if (h->flag == 1)
            return true;
  
        // If we are seeing the node for
        // the first time, mark its flag as 1
        h->flag = 1;
  
        h = h->next;
    }
  
    return false;
}
  
/* Driver program to test above function*/
int main()
{
    /* Start with the empty list */
    struct Node* head = NULL;
  
    push(&head, 20);
    push(&head, 4);
    push(&head, 15);
    push(&head, 10);
  
    /* Create a loop for testing */
    head->next->next->next->next = head;
  
    if (detectLoop(head))
        cout << "Loop found";
    else
        cout << "No Loop";
  
    return 0;
}
// This code is contributed by Geetanjali
输出
Loop found

复杂性分析:

  • 时间复杂度: O(n)。
    只需要遍历一次循环。
  • 辅助空间: O(1)。
    不需要额外的空间。

解决方案 3 :弗洛伊德的寻环算法
方法:这是最快的方法,如下所述:

  • 使用两个指针遍历链表。
  • 将一个指针(slow_p)移动一格,将另一指针(fast_p)移动两格。
  • 如果这些指针在同一个节点相遇,则存在一个循环。如果指针不满足,则链表没有循环。

下图显示了 detectloop函数在代码中的工作方式:

Floyd 寻环算法的实现:

C++

// C++ program to detect loop in a linked list
#include 
using namespace std;
  
/* Link list node */
class Node {
public:
    int data;
    Node* next;
};
  
void push(Node** head_ref, int new_data)
{
    /* allocate node */
    Node* new_node = new Node();
  
    /* put in the data */
    new_node->data = new_data;
  
    /* link the old list off the new node */
    new_node->next = (*head_ref);
  
    /* move the head to point to the new node */
    (*head_ref) = new_node;
}
  
int detectLoop(Node* list)
{
    Node *slow_p = list, *fast_p = list;
  
    while (slow_p && fast_p && fast_p->next) {
        slow_p = slow_p->next;
        fast_p = fast_p->next->next;
        if (slow_p == fast_p) {
            return 1;
        }
    }
    return 0;
}
  
/* Driver code*/
int main()
{
    /* Start with the empty list */
    Node* head = NULL;
  
    push(&head, 20);
    push(&head, 4);
    push(&head, 15);
    push(&head, 10);
  
    /* Create a loop for testing */
    head->next->next->next->next = head;
    if (detectLoop(head))
        cout << "Loop found";
    else
        cout << "No Loop";
    return 0;
}
  
// This is code is contributed by rathbhupendra
输出
Loop found

复杂性分析:

  • 时间复杂度: O(n)。
    只需要遍历一次循环。
  • 辅助空间: O(1)。
    不需要空间。

上述算法如何工作?
请参阅:弗洛伊德的慢速和快速指针方法如何工作?
https://www.youtube.com/watch?v=Aup0kOWoMVg

解决方案4 :在不修改链表数据结构的情况下标记访问过的节点
在此方法中,会创建一个临时节点。使遍历的每个节点的下一个指针指向这个临时节点。这样,我们使用节点的下一个指针作为标志来指示该节点是否已被遍历。检查每个节点以查看下一个节点是否指向临时节点。在循环的第一个节点的情况下,我们第二次遍历它时这个条件为真,因此我们发现循环存在。如果我们遇到指向 null 的节点,则循环不存在。

下面是上述方法的实现:

C++

// C++ program to return first node of loop
#include 
using namespace std;
  
struct Node {
    int key;
    struct Node* next;
};
  
Node* newNode(int key)
{
    Node* temp = new Node;
    temp->key = key;
    temp->next = NULL;
    return temp;
}
  
// A utility function to print a linked list
void printList(Node* head)
{
    while (head != NULL) {
        cout << head->key << " ";
        head = head->next;
    }
    cout << endl;
}
  
// Function to detect first node of loop
// in a linked list that may contain loop
bool detectLoop(Node* head)
{
  
    // Create a temporary node
    Node* temp = new Node;
    while (head != NULL) {
  
        // This condition is for the case
        // when there is no loop
        if (head->next == NULL) {
            return false;
        }
  
        // Check if next is already
        // pointing to temp
        if (head->next == temp) {
            return true;
        }
  
        // Store the pointer to the next node
        // in order to get to it in the next step
        Node* nex = head->next;
  
        // Make next point to temp
        head->next = temp;
  
        // Get to the next node in the list
        head = nex;
    }
  
    return false;
}
  
/* Driver program to test above function*/
int main()
{
    Node* head = newNode(1);
    head->next = newNode(2);
    head->next->next = newNode(3);
    head->next->next->next = newNode(4);
    head->next->next->next->next = newNode(5);
  
    /* Create a loop for testing(5 is pointing to 3) */
    head->next->next->next->next->next = head->next->next;
  
    bool found = detectLoop(head);
    if (found)
        cout << "Loop Found";
    else
        cout << "No Loop";
  
    return 0;
}
输出
Loop Found

复杂性分析:

  • 时间复杂度: O(n)。
    只需要遍历一次循环。
  • 辅助空间: O(1)。
    不需要空间。

解决方案 5:存储长度

在这个方法中,创建了两个指针,第一个(总是指向头部)和最后一个。每次最后一个指针移动时,我们都会计算第一个和最后一个之间的节点数,并检查当前节点数是否 > 以前的节点数,如果是,我们继续移动最后一个指针,否则意味着我们已经到达循环的末尾,所以我们相应地返回输出。

C++

// C++ program to return first node of loop
#include 
using namespace std;
  
struct Node {
    int key;
    struct Node* next;
};
  
Node* newNode(int key)
{
    Node* temp = new Node;
    temp->key = key;
    temp->next = NULL;
    return temp;
}
  
// A utility function to print a linked list
void printList(Node* head)
{
    while (head != NULL) {
        cout << head->key << " ";
        head = head->next;
    }
    cout << endl;
}
  
/*returns distance between first and last node every time
 * last node moves forwards*/
int distance(Node* first, Node* last)
{
    /*counts no of nodes between first and last*/
    int counter = 0;
  
    Node* curr;
    curr = first;
  
    while (curr != last) {
        counter += 1;
        curr = curr->next;
    }
  
    return counter + 1;
}
  
// Function to detect first node of loop
// in a linked list that may contain loop
bool detectLoop(Node* head)
{
  
    // Create a temporary node
    Node* temp = new Node;
  
    Node *first, *last;
  
    /*first always points to head*/
    first = head;
    /*last pointer initially points to head*/
    last = head;
  
    /*current_length stores no of nodes between current
     * position of first and last*/
    int current_length = 0;
  
    /*current_length stores no of nodes between previous
     * position of first and last*/
    int prev_length = -1;
  
    while (current_length > prev_length && last != NULL) {
        // set prev_length to current length then update the
        // current length
          prev_length = current_length;
        // distance is calculated
        current_length = distance(first, last);
        // last node points the next node
        last = last->next;
    }
      
    if (last == NULL) {
        return false;
    }
    else { 
        return true;
    }
}
  
/* Driver program to test above function*/
int main()
{
    Node* head = newNode(1);
    head->next = newNode(2);
    head->next->next = newNode(3);
    head->next->next->next = newNode(4);
    head->next->next->next->next = newNode(5);
  
    /* Create a loop for testing(5 is pointing to 3) */
    head->next->next->next->next->next = head->next->next;
  
    bool found = detectLoop(head);
    if (found)
        cout << "Loop Found";
    else
        cout << "No Loop Found";
  
    return 0;
}
输出
Loop Found

复杂性分析:

  • 时间复杂度: O(n 2 )
  • 辅助空间: O(1)

另一种方法:

  1. 这是给定问题的最简单方法,我们唯一要做的就是为链表中不在给定范围内的节点的每个数据分配一个新值。
  2. 例子 假设(1 <= 节点上的数据 <= 10^3)然后在访问节点后将数据分配为 -1,因为它超出了给定范围。

请按照下面给出的代码更好地理解:

C++

// C++ program to return first node of loop
#include 
using namespace std;
  
struct Node {
    int key;
    struct Node* next;
};
  
Node* newNode(int key)
{
    Node* temp = new Node;
    temp->key = key;
    temp->next = NULL;
    return temp;
}
  
// Function to detect first node of loop
// in a linked list that may contain loop
bool detectLoop(Node* head)
{
      
    // If the head is null we will return false
    if (!head)
        return 0;
    else {
        
        // Traversing the linked list 
        // for detecting loop
        while (head) {
            // If loop found
            if (head->key == -1) {
                return true;
            }
            
            // Changing the data of visited node to any
            // value which is outside th given range here it
            // is supposed the given range is (1 <= Data on
            // Node <= 10^3)
            else {
                head->key = -1;
                head = head->next;
            }
        }
        // If loop not found return false
        return 0;
    }
}
  
/* Driver program to test above function*/
int main()
{
    Node* head = newNode(1);
    head->next = newNode(2);
    head->next->next = newNode(3);
    head->next->next->next = newNode(4);
    head->next->next->next->next = newNode(5);
  
    /* Create a loop for testing(5 is pointing to 3) */
    head->next->next->next->next->next = head->next->next;
  
    bool found = detectLoop(head);
    cout << found << endl;
    return 0;
}
输出
1

时间复杂度: O(N)

辅助空间: O(1)

有关详细信息,请参阅链接列表中有关检测循环的完整文章!