[算法|C++]如何判断链表有环

参考:《漫画算法-小灰的算法之旅》

目录

题目

解决方法:

代码

问题扩展


题目

有一个单向链表,链表中可能出现“环”,如下图所示。那么如何用程序来判断该链表是否为有环链表呢?

解决方法:

可以使用两盒指针来解决这个问题:

首先创建两个指针p1和p2,让它们同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针p1每次向后移动一个节点,让指针p2每次向后移动2个节点,然后比较两个指针指向的节点是否相同。如果相同,则可以判断出链表有环,如果不同,则继续下一次循环。

第1步:p1和p2都指向节点5。

 第2步:p1指向节点3,p2指向节点7。

 第3步:p1指向节点7,p2指向节点6。

 第4步:p1指向节点2,p2指向节点1。

 第5步,p1指向节点6,p2也指向节点6,p1和p2所 指相同,说明链表有环。

此方法就类似于一个追及问题。在一个环形跑道上,两个运动员从同一地点起跑,一个运动员速度快,另一个运动员速度慢。当两 人跑了一段时间后,速度快的运动员必然会再次追上并超过速度慢的运动员,原因很简单,因为跑道是环形的。

假设链表的节点数量为n,则该算法的时间复杂度 为O(n)。除两个指针外,没有使用任何额外的存储空间,所以空间复杂度是O(1)。

代码

using namespace std;
#include<iostream>
#include<string>
//创建链表节点结构体
struct Node {
	int data;  //节点数据
	Node* next;  //指向下一个节点的指针
	Node(int _data):data(_data),next(nullptr) {
	}
	//定义了一个构造函数,该构造函数带有一个整型参数 _data,表示该节点的数据。构造函数使用了成员初始化列表,
	//将节点的数据成员 data 初始化为 _data,将节点的指针成员 next 初始化为 nullptr,表示该节点的下一个节点为空指针。
};
//判断是否有环
int isCycle(Node* head) {
	Node* p1 = head;
	Node* p2 = head;
	while (p2 != nullptr && p2->next!= nullptr) {
		p1 = p1->next;  //p1向后移动一个节点
		p2 = p2->next ->next;    //p2向后移动两个节点
		if (p1 == p2) {
			return true;
		}
	}
	return false;
}

int main()
{

	Node* node1 = new Node(5);
	Node* node2 = new Node(3);
	Node* node3 = new Node(7);
	Node* node4 = new Node(2);
	Node* node5 = new Node(6);
	Node* node6 = new Node(8);
	Node* node7 = new Node(1);
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = node5;
	node5->next = node6;
	node6->next = node7;
	node7->next = node4;
	
	cout << isCycle(node1) << endl;
	system("pause");
	return 0;
}

问题扩展

如果链表有环,如何求出环的长度?

 解:

当两个指针首次相遇,证明链表有环的时候,让两个指针从相遇点i继续循环前进,并统计前进的循环次数,直到两个指针第二次相遇。此时,统计出来的前进次数就是环长。因为指针p1每次走1步,指针p2每次走2步,两者的速度差是1步。当两个指针再次相遇时,p2比p1多走了整整1圈。因此,环长=每一次速度差×前进次数=前进次数。

猜你喜欢

转载自blog.csdn.net/weixin_45922730/article/details/129776873