链表是否有环,以及是否有公共结点

第一部分——判断单个链表是否有环
使用两个指针,一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步;
若快指针最后变为空,则单链表为无环单链表,返回空指针;
若快慢指针在某一时刻指向同一节点,即二者完全一样,则为有环单链表,
此时让快指针重新指向链表头结点,然后快慢指针均一次走一步,
当快慢指针再次相同时,则此节点即为链表入环节点,将其返回。
第二部分——判断链表是否相交
情况一:两链表中一个为有环链表,一个为无环链表,则此时两链表一定不相交,返回空即可;
情况二:两个链表都是无环链表,此时有两种情况,1)两个无环链表相交;2)两个无环链表不相交;
首先遍历两链表,分别得到两个链表的长度,
取两个长度的差值,然后让长链表先遍历差值长度,此时长链表剩余部分与短链表长度相同,
然后两条链表同时遍历,直到遇到相同节点为止,若相同节点为空,则两链表不相交,返回空,
否则两链表相交,返回相交节点即可;
情况三:两个链表都是有环链表,此时有三种情况,1)两个有环链表不相交;2)两个有环链表相交,且交点在环外;3)两个有环链表相交,且交点在环内。
首先获得两个链表的入环节点,若入环节点相同,则可转变为情况二,此时将入环节点作为链表的终止节点即可;若两个入环节点不同,以一个入环节点开始遍历,若遍历一遍过程中都没有遇见另一个入环节点,则两链表不相交,返回空,若遇到另一入环节点,则说明两链表相交,此时返回任意一个入环节点即可。

其他文章:https://blog.csdn.net/computer_user/article/details/86833392
https://blog.csdn.net/qq_30024069/article/details/96428037
https://blog.csdn.net/fangfang_666/article/details/76045987

struct ListNode
{
	int value;
	struct ListNode* next;
	ListNode(int v = 0) :value(v), next(nullptr) {}
};

//判断是否有环,如果有环,返回环结点入口,如果没有环,则返回nullptr
ListNode* isLoop(ListNode* head)
{
	if (head == nullptr || head->next == nullptr||head->next->next==nullptr)
		return nullptr;
	ListNode* slow = head->next;  //慢指针走一步
	ListNode* fast = head->next->next;  //快指针走俩步
	while (slow != fast)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow->next == nullptr || fast->next == nullptr)
			return nullptr;
	}
	fast = head; //fast指针回到原点
	while (slow != fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

//计算带环链表的长度
int loopLength(ListNode* head, ListNode* enterLoop)
{
	int count = 1; //代表头结点长度为1
	while (head != enterLoop)
	{
		count++;
		head = head->next;
	}
	ListNode* cur = enterLoop->next;
	while (cur != enterLoop)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

//计算无环链表的长度
int noloopLength(ListNode* head)
{
	int count = 0;
	if (head == nullptr)
		return count;
	while (head != nullptr)
	{
		count++;
		head = head->next;
	}
	return count;
}

//计算指定俩个结点之间的长度
int calculateCount(ListNode* first, ListNode* second)
{
	int count = 0;
	while (first != second)
	{
		count++;
		first = first->next;
	}
	return count;
}

//判断俩个无环链表是否有公共结点(是否相交)
ListNode* noloopListMeet(ListNode* head1, int len1, ListNode* head2, int len2)
{
	if (len1 < len2)
		swap(head1, head2);
	int k = abs(len1 - len2); //计算出俩个链表之间的差值
	//让长的那个链表先走完差值
	while (k--)
		head1 = head1->next;
	while (head1 != head2) //都为nullptr或者有公共结点时退出
	{
		head1 = head1->next;
		head2 = head2->next;
	}
	return head1;
}

//判断俩个有环链表是否有公共结点(是否相交)
ListNode* loopListMeet(ListNode* head1, ListNode* loop1, ListNode* head2, ListNode* loop2)
{
	if (loop1 == loop2) //俩个有环链表的入口结点相同
	{
		return noloopListMeet(head1, calculateCount(head1, loop1), head2, calculateCount(head2, loop2));
	}
	else  //否则看看环中是否能相交
	{
		ListNode* temp = loop1;
		loop1 = loop1->next;
		while (temp != loop1&&temp != loop2)
		{
			temp = temp->next;
		}
		if (temp == loop2)
			return temp;
		else
			return nullptr;
	}
}

int main()
{
	ListNode* node1 = new ListNode(1);
	ListNode* node2 = new ListNode(2);
	ListNode* node3 = new ListNode(3);
	ListNode* node4 = new ListNode(4);
	ListNode* node5 = new ListNode(5);
	ListNode* node6 = new ListNode(6);

	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = node5;
	node5->next = node6;
	node6->next = node2;

	ListNode* node = isLoop(node1);
	if (node)
	{
		cout << "环入口值为:"<<node->value << endl;
		cout << "带环链表长度为:" << loopLength(node1, node) << endl;
	}

	ListNode* node7 = new ListNode(7);
	ListNode* node8 = new ListNode(8);
	ListNode* node9 = new ListNode(9);
	ListNode* node10 = new ListNode(10);
	ListNode* node11 = new ListNode(11);
	ListNode* node12 = new ListNode(12);
	ListNode* node13 = new ListNode(13);
	ListNode* node14 = new ListNode(14);

	node13->next = node14;  //用于测试俩个不相交的无环链表

	node7->next = node8;
	node8->next = node9;
	node9->next = node10;
	node10->next = node11;
	node12->next = node10;

	ListNode* cross = noloopListMeet(node7, noloopLength(node7), node12, noloopLength(node12));
	if (cross)
		cout << "公共结点为:" << cross->value << endl;
	else
		cout << "俩个链表不相交" << endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值