链表面试题之链表相交问题

判断两个链表是否相交以及如果相交则求出交点

  @问题分析

      首先最容易想到也是最容易求解的就是不带环的链表相交问题啦!在这里我提供一种不带环链表判断相交的思路:既然两条链表相交且不带环,那仫两条链表的最后一个结点必然是同一个结点,那仫我们就可以通过判断两条链表的最后一个结点来判断链表是否相交的问题了, 假设其中一条链表有m个结点而另一条链表有n个结点,这种方法的时间复杂度为O(m+n) 
      在判断了不带环链表相交问题之后如何求出相交点呢?在这里我提供两种思路,在代码实现部分这两种思路都会被实现,思路如下:
      想法一:
      找到其中一条链表的尾结点让它的next指针域指向另一条链表的第一个结点,这样就构成了环,此时通过上述求环的入口点就可以求出构成环的入口点,这个入口点就是不带环链表的相交点;
      想法二:
      先分别统计出两条链表中的结点总数count1,count2,此时让长的链表先从头走(count1-count2)步(假设链表1长于链表2),然后两个指针一起向后走直到两个指针指向同一个结点,此时这个结点就是不带环链表的相交点;
      那仫判断不带环的链表相交问题就结束了,那仫带环的呢?我总结了以下几种链表相交的情况:
      
        通过上图我们可以发现如果通过调用函数Check_Cycle 则可以找到带环链表的相遇点
        1).如果一条链表带环,一条链表不带环,那仫这两条链表必然是不相交
        2).如果两条链表都带环则属于上述的情况三,此时如何区分这三种情况呢?通过观察发现,第一种情况带环但是不相交,后两种情况带环属于相交的情况;我们知道只要链表带环则必然可以求出相遇点的,此时就可以根据判断两个相遇点是否在一个环内来排除情况一啦...如果两个相遇点在同一个环内,此时就要区分交点在环内和交点在环外的情况了:如果交点在环外则两条链表公用同一个环则只有一个入口点,如果交点在环外虽然他们也公用同一个环但是通过上图发现此时有两个入口点,这种情况的交点就是这两个入口点之一选择其中的一个输出就可以了;
        3).在上述情况中通过判断入口点的方式区分了交点在环内和交点在环外两种情况,那仫如何求出交点在环外情况的交点呢?当然在之前已经求过了环的入口点,如果我们让环的入口点指向另一条链表的第一个结点(构成新环)则此时再求出环的入口点,这个入口点就是交点;
       

    

 @ 代码实现部分:

       不带环的链表相交和求交点的两种方法实现:
       
int Check_Cross(pLinkList plist1,pLinkList plist2)
{
	//判断链表是否相交,两个链表均不带环
	pLinkNode p1=plist1->phead;
	pLinkNode p2=plist2->phead;
	if((p1 == NULL)||(p2 == NULL))  //空链表
	{
		return -1;
	}
	while(p1->next)
	{
		p1=p1->next;
	}
	while(p2->next)
	{
		p2=p2->next;
	}
	//p1,p2分别指向链表plist1,plist2的最后一个结点
	//通过判断最后一个结点是否相同来判断两条链表是否相交
	if(p1 == p2)
		return 1;   //相交
	return 0;
}

pLinkNode GetLink_EntryNode(pLinkList plist1,pLinkList plist2)
{
	//统计出两个链表的结点总数count1,count2
	//让较长的链表走count1-count2步
	//两个链表一起走直到相遇则返回相遇结点的地址
	int count1=0;
	int count2=0;
	pLinkNode cur1=NULL;
	pLinkNode cur2=NULL;
	assert(plist1);
	assert(plist2);
	cur1=plist1->phead;
	cur2=plist2->phead;
	while(cur1)  //统计链表1的总结点数
	{
		count1++;
		cur1=cur1->next;
	}
	while(cur2)  //统计链表2的总结点数
	{
		count2++;
		cur2=cur2->next;
	}
	cur1=plist1->phead;
	cur2=plist2->phead;
	if(count1-count2 >= 0)
	{
		while(count1-count2 != 0)
		{
			cur1=cur1->next;
			count1--;
		}
		if(cur1 != cur2)
		{
			cur1=cur1->next;
			cur2=cur2->next;
		}
	}
	else
	{
		while(count2-count1 != 0)
		{
			cur2=cur2->next;
			count2--;
		}
		if(cur1 != cur2)
		{
			cur1=cur1->next;
			cur2=cur2->next;
		}
	}
	return cur1;
}

pLinkNode _GetLink_EntryNode(pLinkList plist1,pLinkList plist2)
{
	//构造成环的方法求两个链表的交点
	pLinkNode meet=NULL;
	pLinkNode cur=NULL;
	assert(plist1);
	assert(plist2);
	cur=plist1->phead;
	//构造环
	while(cur->next)
	{
		cur=cur->next;
	}
	cur->next=plist2->phead;
	//找出环的相遇结点
	meet=Check_Cycle(plist1);
	//求出环的入口点
	return GetCircle_EntryNode(plist1,meet);
}

      带环的链表判断相交和求交点的方法:
      
int _Check_Cross(pLinkNode meet1,pLinkNode meet2)
	//两条链表都带环判断链表是否相交
{
	pLinkNode m1=meet1;
	pLinkNode m2=meet2;
	while(m1 != m2)
	{
		m1=m1->next;
	}
	if(m1 == m2)
		return 1;//链表相交
	return 0;             
}

pLinkNode EntryNode(pLinkList plist1,pLinkList plist2,pLinkNode start1)
{
	pLinkNode s=start1;
	pLinkNode meet=NULL;
	assert(plist1);
	assert(plist2);
	while(s->next != start1)
	{
		s=s->next;
	}
	s->next=plist2->phead;
	meet=Check_Cycle(plist1);
	return GetCircle_EntryNode(plist1,meet);
}

       
       测试代码部分:
       
void text10()
{
	int ret=0;
	LinkList plist1;
	LinkList plist2;
	pLinkNode meet1=NULL;  
	pLinkNode meet2=NULL;
	pLinkNode start1=NULL;
	pLinkNode start2=NULL;
	pLinkNode tmp=NULL;
	Init_LinkList(&plist1);
	Push_back(&plist1,1);
	Push_back(&plist1,3);
	Push_back(&plist1,5);
	Push_back(&plist1,7);
	Push_back(&plist1,9);
	Push_back(&plist1,11);
	Find_NUM(&plist1,11)->next=Find_NUM(&plist1,5); //链表1构造成环
	Init_LinkList(&plist2);
	Push_back(&plist2,2);
	Push_back(&plist2,4);
	//Find_NUM(&plist2,4)->next=Find_NUM(&plist1,7);  //交点在环内
	Find_NUM(&plist2,4)->next=Find_NUM(&plist1,3);  //交点在环外
	//1
	//Init_LinkList(&plist1);   //两条链表都不带环
	//Push_back(&plist1,9);
	//Push_back(&plist1,1);
	//Push_back(&plist1,3);
	//Push_back(&plist1,5);
	//Push_back(&plist1,7);
	//printf("链表1的元素为:");
	//Print_LinkList(&plist1);
	//2
	//Init_LinkList(&plist2);
	//Push_back(&plist2,2);
	//链表2与链表1公用同交点以后的结点
	//Find_NUM(&plist2,2)->next=Find_NUM(&plist1,3);
	//printf("链表2的元素为:");
	//Print_LinkList(&plist2);
	meet1=Check_Cycle(&plist1);
	meet2=Check_Cycle(&plist2);
	if(meet1 == NULL && meet2 == NULL)
	{
		//两条链表都不带环
		ret=Check_Cross(&plist1,&plist2);
		if(ret == 1)
		{
			printf("两个链表相交\n");
			//tmp=GetLink_EntryNode(&plist1,&plist2);
			tmp=_GetLink_EntryNode(&plist1,&plist2);
			printf("链表的相交点为:%d\n",tmp->data);
		}
		else if(ret == 0)
		{
			printf("两个链表不相交\n");
		}
		else
		{
			printf("存在空链表\n");
		}
	}
	else if((meet1 == NULL && meet2 != NULL) || (meet1 != NULL && meet2 == NULL))
	{
		//一条链表带环一条链表不带环
		printf("两个链表不相交\n");
	}
	else
	{
		//两条链表都带环
		ret=_Check_Cross(meet1,meet2);
		if(ret == 1)
		{
			printf("两个链表相交\n");
			start1=GetCircle_EntryNode(&plist1,meet1);
			start2=GetCircle_EntryNode(&plist2,meet2);
			if(start1 == start2)  //交点在环外
			{
				tmp=EntryNode(&plist1,&plist2,start1);
				printf("链表的相交点为:%d\n",tmp->data);
			}
			else   //交点在环内
			{
				printf("链表的相交点为:%d\n",start2->data);
			}
		}
		else
		{
			printf("两个链表不相交\n");
		}
	}
	free(meet1);
	free(meet2);
}

       在上述代码实现中用到了判断链表是否带环和求环的入口点的函数,具体实现见:
       
          既然选择了就坚持下去,相信自己...
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值