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

Posted 职坐标在线

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++丨如何检查链表中的循环?这5个方案,真是太绝了!相关的知识,希望对你有一定的参考价值。

是新朋友吗?记得先点蓝字关注我哦~

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

今日课程菜单


Java全栈开发 | Web前端+H5

大数据开发 | 数据分析

 人工智能+Python | 人工智能+物联网


来源:小职(z_zhizuobiao)

找我:✅ 解锁高薪工作 ✅ 免费获取干货教程


通过5个解决方案教你C++中检测链表中的循环,快来看看,是否对你有帮助!


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

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


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


解决方案1:散列方法

#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++课程从入门到实战项目,让你快速掌握企业所需前沿技术,助你在6个月挑战高薪入职。


我是小职,记得找我

✅ 解锁高薪工作

✅ 免费获取最新技术干货教程资源

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

往期回顾






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

更多精彩内容“阅读原文”

转发你最帅,点赞涨薪快

以上是关于C++丨如何检查链表中的循环?这5个方案,真是太绝了!的主要内容,如果未能解决你的问题,请参考以下文章

链表循环检测算法

反转链表中的每 k 个节点块

有 10 万个乱序的数,如何取前 5 个最大(或最小)的数?丨堆排序链表实现局部排序

为啥在链表中查找循环时将指针增加 2,为啥不增加 3、4、5?

面试:删除链表中的循环 - Java

如何检测链表中的循环?