判断两个单链表有相交节点/判断一个链表是否有环

1、题目:给定两个单向链表的头指针,如h1和h2,判断这两个链表是否有相交节点。

假设两个链表都不带环,其相交如下图所示:


一、分析与解法

两个链表相交的情况在一个大的系统中可能会出现,如果出现两个链表相交的情况,一旦程序释放了其中一个链表的所有节点,那样就会造成信息的丢失,并且另一个与之相交的链表也会受到影响。在特殊情况下,的确需要出现相交的两个链表,我们希望在释放一个链表之前知道是否有其他链表跟当前这个链表相交。


解法一、一般解法

双重遍历两个链表,直接查找是否有相同的节点地址,其时间复杂度为O(len(h1)*len(h2)),空间复杂度为O(1)。这种方法很耗时间。


解法二、hash计数

如果两个链表相交,那么这两个链表中就会有共同的节点。而节点地址又是节点的唯一标识。所以,如果我们能够判断两个链表中是否存在地址一致的节点,就可以知道这两个链表是否相交。一个简单的做法就是对第一个链表的节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果它在hash表中出现,那么说明第二个链表和第一个链表有共同的节点。这个方法的时间复杂度为O(len(h1)+len(h2)),空间复杂度为O(len(h1))。它是典型地以增加存储空间为代价,减少了时间复杂度。


解法三、转化为一个链表是否有环问题

由于两个链表都没有环,那我们可以把第二个链表接在第一个链表的后面,如果得到的链表有环,则说明这两个链表不相交。这样就把问题转化为判断一个链表是否有环的问题了。


判断一个链表是否有环,也不是简单的问题。其时间复杂度为O(len(h1)+len(h2)),空间复杂度为O(1)。


解法四、尾节点开始

如果两个没有环的链表相较于某一个节点,那么在这个节点之后的所有节点都是两个链表所共有的。根据这个特点,我们通过找到链表最后一个节点进行判断比较。先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了 一个复杂度为O(len(h1)+len(h2)),且只用了一个额外的指针来存储最后一个节点。

代码实现:

/*
	判断两个不含环的单链表相交
	相交指的是结点的地址相同,而不是结点的值相同
*/
#include <iostream>

using namespace std;

struct LinkList{
	int value;
	LinkList *next;
};

//建立一个链表
void InsertList(LinkList *&list)
{
	LinkList *head;
	LinkList *newNode;
	int data;
	head=list;
	while(head->next)
		head=head->next;
	cout<<"输入数据:";
	while(1)
	{
		cin>>data;
		if(data == 0) break;
		newNode=new LinkList();
		newNode->value = data;
		newNode->next = NULL;
		head->next = newNode;
		head=newNode;
		head->next=NULL;
	}
}

//输出链表
void Traverse(LinkList *list)
{
	LinkList *p;
	p=list->next;
	while(p)
	{
		cout<<" "<<p->value<<" address="<<p<<endl;
		p=p->next;
	}
}

int main()
{
	LinkList *first,*second;
	LinkList *fhead,*shead,*h1,*h2,*p,*q;
	int flen=0,slen=0;

	first=new LinkList();
	first->next=NULL;
	second=new LinkList();
	second->next=NULL;

	InsertList(first);
	InsertList(second);
	//将第一个链表中从第四个结点起链接到第二个链表后,构成两个相交的链表
	p=second;
	while(p->next)
		p=p->next;

	q=first;
	for(int i=0;i<4;i++)
		q=q->next;
	p->next=q;

	Traverse(first);
	cout<<endl;
	Traverse(second);
	cout<<endl;

	//对链表1进行遍历到尾部
	fhead=first;
	while(fhead->next)
	{
		fhead=fhead->next;
		flen++;
	}

	//对链表2进行遍历到尾部
	shead=second;
	while(shead->next)
	{
		shead=shead->next;
		slen++;
	}

	if(fhead == shead)
	{
		cout<<"两个链表相交!"<<endl;
	}
	else
		cout<<"两个链表不相交!"<<endl;
	system("pause");
	return 0;
}


二、拓展问题

2.1、求出两个链表相交的第一个节点?

解法:计算相差的长度

分别遍历两个链表,记录长度(若已知则不需此步),短链表从头结点开始,长链表从第| len(h1) - len(h2) | 个节点开始,依次遍历并比较,相等的第一个节点则为相交的第一个节点。也可以使用hash法。

代码实现:

//求两个链表相交的第一个节点
	if(flen>=slen)//其两个链表的长度差值
	{
		len=flen-slen;
		while(len--)
			h1=h1->next;
	}
	else
	{
		len=slen-flen;
		while(len--)
			h2=h2->next;
	}
	while(1)
	{
		if(h1 == h2)
		{
			cout<<"两个链表第一个相交的节点是"<<h1->value<<",地址是:"<<h1<<endl;
			break;
		}
		else if((h1->next)&&(h2->next))
		{
			h1=h1->next;
			h2=h2->next;
		}
		else
			cout<<"两个链表不相交!"<<endl;
	}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值