五個解決辦法教你C++中檢測鏈表中的循環
給定一個鏈表,檢查鏈表是否有循環。下圖顯示了帶有循環的鏈表。

以下是執行此操作的不同方法
解決方案1:散列方法:
遍歷該列表,并將節點地址始終放在哈希表中。在任何時候,如果達到NULL,則返回false,如果當前節點的下一個指向Hash中先前存儲的任何節點,則返回true。
- #include <bits/stdc++.h>
- using namespace std;
- struct Node {
- int data;
- struct Node* next;
- };
- void push(struct Node** head_ref, int new_data)
- {
- struct Node* new_node = new Node;
- new_node->data = new_data;
- new_node->next = (*head_ref);
- (*head_ref) = new_node;
- }
- bool detectLoop(struct Node* h)
- {
- unordered_set<Node*> s;
- while (h != NULL) {
- if (s.find(h) != s.end())
- return true;
- s.insert(h);
- h = h->next;
- }
- return false;
- }
- int main()
- {
- struct Node* head = NULL;
- push(&head, 20);
- push(&head, 4);
- push(&head, 15);
- push(&head, 10);
- head->next->next->next->next = head;
- if (detectLoop(head))
- cout << "Loop found";
- else
- cout << "No Loop";
- return 0;
- }
復雜度分析:
時間復雜度: O(n)。
只需循環一次即可。
輔助空間: O(n)。
n是將值存儲在哈希圖中所需的空間。
解決方案2:通過修改鏈表數據結構,無需哈希圖即可解決此問題。
方法:此解決方案需要修改基本鏈表數據結構。
- 每個節點都有一個訪問標志。
- 遍歷鏈接列表并繼續標記訪問的節點。
- 如果您再次看到一個訪問過的節點,那么就會有一個循環。該解決方案適用于O(n),但每個節點都需要其他信息。
- 此解決方案的一種變體不需要修改基本數據結構,可以使用哈希來實現,只需將訪問的節點的地址存儲在哈希中,如果您看到哈希中已經存在的地址,則存在一個循環。
C++:
- #include <bits/stdc++.h>
- using namespace std;
- struct Node {
- int data;
- struct Node* next;
- int flag;
- };
- void push(struct Node** head_ref, int new_data)
- {
- struct Node* new_node = new Node;
- new_node->data = new_data;
- new_node->flag = 0;
- new_node->next = (*head_ref);
- (*head_ref) = new_node;
- }
- bool detectLoop(struct Node* h)
- {
- while (h != NULL) {
- if (h->flag == 1)
- return true;
- h->flag = 1;
- h = h->next;
- }
- return false;
- }
- int main()
- {
- struct Node* head = NULL;
- push(&head, 20);
- push(&head, 4);
- push(&head, 15);
- push(&head, 10);
- head->next->next->next->next = head;
- if (detectLoop(head))
- cout << "Loop found";
- else
- cout << "No Loop";
- return 0;
- }
復雜度分析:
時間復雜度: O(n)。
只需循環一次即可。
輔助空間: O(1)。
不需要額外的空間。
解決方案3:Floyd的循環查找算法
方法:這是最快的方法,下面進行了介紹:
- 使用兩個指針遍歷鏈表。
- 將一個指針(slow_p)移動一個,將另一個指針(fast_p)移動兩個。
- 如果這些指針在同一節點相遇,則存在循環。如果指針不符合要求,則鏈接列表沒有循環。
Floyd的循環查找算法的實現:
- #include <bits/stdc++.h>
- using namespace std;
- class Node {
- public:
- int data;
- Node* next;
- };
- void push(Node** head_ref, int new_data)
- {
- Node* new_node = new Node();
- new_node->data = new_data;
- new_node->next = (*head_ref);
- (*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;
- }
- int main()
- {
- Node* head = NULL;
- push(&head, 20);
- push(&head, 4);
- push(&head, 15);
- push(&head, 10);
- head->next->next->next->next = head;
- if (detectLoop(head))
- cout << "Loop found";
- else
- cout << "No Loop";
- return 0;
- }
解決方案4:在不修改鏈接列表數據結構的情況下標記訪問的節點
在此方法中,將創建一個臨時節點。使遍歷的每個節點的下一個指針指向該臨時節點。這樣,我們將節點的下一個指針用作標志來指示該節點是否已遍歷。檢查每個節點以查看下一個節點是否指向臨時節點。在循環的第一個節點的情況下,第二次遍歷該條件將成立,因此我們發現該循環存在。如果遇到一個指向null的節點,則循環不存在。
下面是上述方法的實現:
- #include <bits/stdc++.h>
- 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;
- }
- void printList(Node* head)
- {
- while (head != NULL) {
- cout << head->key << " ";
- head = head->next;
- }
- cout << endl;
- }
- bool detectLoop(Node* head)
- {
- Node* temp = new Node;
- while (head != NULL) {
- if (head->next == NULL) {
- return false;
- }
- if (head->next == temp) {
- return true;
- }
- Node* nex = head->next;
- head->next = temp;
- head = nex;
- }
- return false;
- }
- 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);
- head->next->next->next->next->next = head->next->next;
- bool found = detectLoop(head);
- if (found)
- cout << "Loop Found";
- else
- cout << "No Loop";
- return 0;
- }
復雜度分析:
時間復雜度: O(n)。
只需循環一次即可。
輔助空間: O(1)。
不需要空間。
解決方案5:存放長度
在此方法中,將創建兩個指針,第一個(始終指向頭)和最后一個指針。每次最后一個指針移動時,我們都會計算第一個和最后一個之間的節點數,并檢查當前節點數是否大于先前的節點數,如果是,我們通過移動最后一個指針進行操作,否則就意味著我們已經到達循環的終點,因此我們相應地返回輸出。
- #include <bits/stdc++.h>
- 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;
- }
- void printList(Node* head)
- {
- while (head != NULL) {
- cout << head->key << " ";
- head = head->next;
- }
- cout << endl;
- }
- int distance(Node* first, Node* last)
- {
- int counter = 0;
- Node* curr;
- curr = first;
- while (curr != last) {
- counter += 1;
- curr = curr->next;
- }
- return counter + 1;
- }
- bool detectLoop(Node* head)
- Node* temp = new Node;
- Node *first, *last;
- first = head;
- last = head;
- int current_length = 0;
- int prev_length = -1;
- while (current_length > prev_length && last != NULL) {
- prev_length = current_length;
- current_length = distance(first, last);
- last = last->next;
- }
- if (last == NULL) {
- return false;
- }
- else {
- return true;
- }
- }
- 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);
- 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;
- }
}