数据结构单链表之检测和删除链表中的循环 | 第十三套

1,590 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

编写一个函数detectAndRemoveLoop() 来检查给定的链表是否包含循环,如果存在循环,则删除循环并返回 true。如果列表不包含循环,则返回 false。下图显示了一个带有循环的链表。detectAndRemoveLoop() 必须将下面的列表更改为 1->2->3->4->5->NULL。

image.png

我们还建议阅读以下帖子作为此处讨论的解决方案的先决条件。编写一个 C 函数来检测链表中的循环 在尝试删除循环之前,我们必须检测它。上面帖子中讨论的技术可用于检测循环。要移除循环,我们需要做的就是获取指向循环最后一个节点的指针。例如,上图中值为 5 的节点。一旦我们有了指向最后一个节点的指针,我们就可以将这个节点的下一个节点设置为 NULL,循环就消失了。 
我们可以轻松地使用哈希或访问节点技术(在上面提到的帖子中讨论过)来获取指向最后一个节点的指针。想法很简单:下一个已被访问(或散列)的第一个节点是最后一个节点。 
我们也可以使用弗洛伊德循环检测算法来检测和删除循环。在弗洛伊德算法中,慢指针和快指针在循环节点相遇。我们可以使用这个循环节点来移除循环。当使用 Floyd 算法进行循环检测时,有以下两种不同的消除循环方法。

方法一(一一查) 我们知道,当快慢指针在一个公共点相遇时,弗洛伊德循环检测算法就终止了。我们也知道这个公共点是循环节点之一(上图中的 2 或 3 或 4 或 5)。将 this 的地址存储在一个指针变量中,比如 ptr2。之后,从链表的头部开始,一一检查节点是否可以从 ptr2 到达。每当我们找到一个可达的节点时,我们就知道这个节点是链表中循环的起始节点,我们可以得到这个节点的前一个节点的指针。

输出:

Linked List after removing loop 
50 20 15 4 10

方法二(更好的解决方案)  

  1. 该方法还依赖于 Floyd's Cycle 检测算法。
  2. 使用 Floyd 循环检测算法检测循环并获取指向循环节点的指针。
  3. 计算循环中的节点数。让计数为k。
  4. 将一个指针固定到头部,另一个指向头部的第 k 个节点。
  5. 以相同的速度移动两个指针,它们将在循环起始节点相遇。
  6. 获取指向循环最后一个节点的指针并将其下一个设为 NULL。
#include <bits/stdc++.h>
using namespace std;

struct Node {
	int data;
	struct Node* next;
};

void removeLoop(struct Node*, struct Node*);

int detectAndRemoveLoop(struct Node* list)
{
	struct 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) {
			removeLoop(slow_p, list);

			/* Return 1 to indicate that loop is found */
			return 1;
		}
	}

	return 0;
}

void removeLoop(struct Node* loop_node, struct Node* head)
{
	struct Node* ptr1 = loop_node;
	struct Node* ptr2 = loop_node;

	unsigned int k = 1, i;
	while (ptr1->next != ptr2) {
		ptr1 = ptr1->next;
		k++;
	}

	ptr1 = head;

	ptr2 = head;
	for (i = 0; i < k; i++)
		ptr2 = ptr2->next;

	while (ptr2 != ptr1) {
		ptr1 = ptr1->next;
		ptr2 = ptr2->next;
	}

	while (ptr2->next != ptr1)
		ptr2 = ptr2->next;

	ptr2->next = NULL;
}

void printList(struct Node* node)
{
	while (node != NULL) {
		cout << node->data << " ";
		node = node->next;
	}
}

struct Node* newNode(int key)
{
	struct Node* temp = new Node();
	temp->data = key;
	temp->next = NULL;
	return temp;
}

int main()
{
	struct Node* head = newNode(50);
	head->next = newNode(20);
	head->next->next = newNode(15);
	head->next->next->next = newNode(4);
	head->next->next->next->next = newNode(10);

	head->next->next->next->next->next = head->next->next;

	detectAndRemoveLoop(head);

	cout << "Linked List after removing loop \n";
	printList(head);
	return 0;
}

输出:

Linked List after removing loop 
50 20 15 4 10 

🥇 往期优质文章

数据结构单链表之链表介绍 | 第一套
数据结构单链表之链表与数组 | 第二套
数据结构单链表之链表插入 | 第三套
数据结构单链表之删除节点 | 第四套
数据结构单链表之删除给定位置的链表节点 | 第五套
数据结构单链表之查看数组与链表的方法 | 第六套-1
数据结构单链表之查看数组与链表的方法 | 第六套-2
数据结构单链表之查找链表的长度(迭代和递归) | 第七套
数据结构单链表之交换链表中的节点而不交换数据 | 第八套
数据结构单链表之反转链表 | 第九套
数据结构单链表之合并两个已排序的链表 | 第十套
数据结构单链表之链表的归并排序 | 第十一套
数据结构单链表之以给定大小的组反转链表 | 第十二套

📣尾注: 想要获取更多数据结构相关的知识,你可以关注我:海拥,我希望你觉得这篇文章有帮助。

如果你看到这里,感谢你的阅读 :)

💌 欢迎大家在评论区提出意见和建议!💌

如果你真的从这篇文章中学到了一些新东西,喜欢它,收藏它并与你的小伙伴分享。🤗最后,不要忘了❤或📑支持一下哦。