C++丨如何检查链表中的循环?这5个方案,真是太绝了!
Posted 职坐标在线
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++丨如何检查链表中的循环?这5个方案,真是太绝了!相关的知识,希望对你有一定的参考价值。
是新朋友吗?记得先点蓝字关注我哦~
今日课程菜单
Java全栈开发 | Web前端+H5
大数据开发 | 数据分析
人工智能+Python | 人工智能+物联网
来源:小职(z_zhizuobiao)
找我:✅ 解锁高薪工作 ✅ 免费获取干货教程
未
完
待
续
通过5个解决方案教你C++中检测链表中的循环,快来看看,是否对你有帮助!
给定一个链表,检查链表是否有循环。下图显示了带有循环的链表。
以下是执行此操作的不同方法:
解决方案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个方案,真是太绝了!的主要内容,如果未能解决你的问题,请参考以下文章
有 10 万个乱序的数,如何取前 5 个最大(或最小)的数?丨堆排序链表实现局部排序