C++丨如何检查链表中的循环?这5个方案,真是太绝了!

通过5个解决方案教你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; 

感谢阅读,希望能帮助到大家,有什么问题欢迎评论区留言。


 

如果你想更好的提升你的编程能力,学好C语言C++编程!弯道超车,快人一步!

C语言C++学习企鹅圈子】,分享(源码、项目实战视频、项目笔记,基础入门教程)

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

编程学习书籍:


 

编程学习视频:


 
原文地址:https://www.cnblogs.com/mu-ge/p/14197207.html