求两个链表的第一个公共节点

参考文献地址

求两个链表的公共节点可分为三步:

  1. 判断链表是否有环
    采用传统的方法,两个指针同时指向头节点,p1一次走一步,p2一次走两步,如果p2走到终点还没有与p1相遇,那么无环,记录下链表的长度并返回空指针。这里要注意的是,p2由于一次走两步,有可能会出现“求null->next”的情况,需要再条件判断中避免。
    如果p1和p2在某个地方相遇了,那相遇的这个地方一定在环上。可以画一个图,形状类似一只勺子。在这里插入图片描述
    从相遇的点和头结点同时开始遍历,这次相遇的点就是环的第一个节点,遍历的长度也就是不在环内的节点的个数。
  2. 判断是否可能存在交点
    头结点相等的单列出来,此时不需要做其他工作,直接返回头结点。
    不可能存在交点的情况要先排除:
    1)存在空链表;
    2)一个有环一个没有环;
    所以可能有公共交点的链表需要满足:两个都无环,或者两个都有环。
  3. 求交点
    1)当两个都无环或者都不是纯环时,需要求的公共交点一定不在环上。
    2)两个都有环的话,排除头结点相等和都不是纯环的情况,即至少有一个是纯环,那么所求的第一个公共交点要么不存在,要么就是纯环的头结点。此时只需要判断纯环的头结点有没有在另一个链表的环上即可。
#include <iostream>
using namespace std;

struct ListNode
{
	int Data;
	ListNode *next;
	ListNode(int val) :
		Data(val),next(nullptr){}
};

ListNode *CreateListNode(int val)
{
	return new ListNode(val);
}

ListNode *Circle(ListNode *head, int &len, bool IsCircle)//判断链表是否有环
{
	ListNode *p1 = head;
	ListNode *p2 = head;
	int step = 1;

	while (p2!=nullptr&&p2->next != nullptr)
	{
		p1 = p1->next;
		p2 = p2->next->next;
		step += 2;

		if (p1 == p2)
			break;
	}

	if (p2 == nullptr)//无环
	{
		len = step-1;
		return nullptr;
	}
	else if (p2->next == nullptr&&p1 != p2)
	{
		len = step;
		return nullptr;
	}
	else
	{
		IsCircle = true;
		p1 = head;
		step = 1;
		while (p1 != p2)
		{
			p1 = p1->next;
			p2 = p2->next;
			step++;
		}
		len = step;
		return p1;
	}
}

ListNode *InserPoint(ListNode *head1, ListNode *head2)
{
	if (head1 == nullptr || head2 == nullptr)//有空链表的情况
		return nullptr;
	if (head1 == head2)//从头结点开始相等的情况
		return head1;
	int len1 = 0, len2 = 0;
	bool IsCircle1 = false, IsCircle2 = false;
	ListNode *CirclePoint1 = Circle(head1, len1, IsCircle1);//分别判断两链表是否有环
	ListNode *CirclePoint2 = Circle(head2, len2, IsCircle2);
	
	if (len1 > 0 && len2 > 0)//俩都没环或都有环并且不是头结点
	{
		int dif = len1 > len2 ? len1 - len2 : len2 - len1;
		ListNode *h1 = len1 > len2 ? head2 : head1;
		ListNode *h2 = len1 > len2 ? head1 : head2;

		for (int i = 0; i < dif; i++)
		{
			h2 = h2->next;
		}
		while (h1 != h2)
		{
			h1++;
			h2++;
		}
		return h1;
	}
	else if (len1 ==0||len2 == 0)//有一个纯环
	{
		ListNode *h1 = len1 > 0 ? head2 : head1;//取纯环的头结点
		ListNode *h2 = len1 > 0 ? head1 : head2;
		int len = len1 > 0 ? len1 : len2;
		h2 += len;
		ListNode *k = h2;
		while (k->next!=h2)
		{
			k++;
			if (h1 == k)
				return h1;
		}
		return nullptr;//没有找到 则不相交
	}
	else//一个有一个没有 则不相交
		return nullptr;
}

int main()
{
	ListNode *h1 = CreateListNode(0);
	ListNode *p = h1;
	for (int i = 1; i < 6; i++)
	{
		ListNode *k = CreateListNode(i);
		p->next = k;
		p = p->next;
	}

	ListNode *h2 = h1->next;

	ListNode *inser = InserPoint(h1, h2);
	cout << inser->Data << endl;

	return 0;
}

代码可能不完善,欢迎指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值