现在的位置: 首页 > 综合 > 正文

面试题—判断链表是否相交

2012年08月16日 ⁄ 综合 ⁄ 共 2230字 ⁄ 字号 评论关闭

         今天看了July的一篇经典文章《程序员编程艺术:第九章、闲话链表追赶问题》,因为现在一直复习数据结构有关链表的算法,顺便总结下,学习下July大牛的判断链表是否相交。出处:http://blog.csdn.net/v_JULY_v 

题目:给出两个单向链表的头指针,判断是否相交。

下面是July文章上面的解法:

分析:这是来自编程之美上的微软亚院的一道面试题目。请跟着我的思路步步深入(部分文字引自编程之美):

  1. 直接循环判断第一个链表的每个节点是否在第二个链表中。但,这种方法的时间复杂度为O(Length(h1) * Length(h2))。显然,我们得找到一种更为有效的方法,至少不能是O(N^2)的复杂度。
  2. 针对第一个链表直接构造hash表,然后查询hash表,判断第二个链表的每个结点是否在hash表出现,如果所有的第二个链表的结点都能在hash表中找到,即说明第二个链表与第一个链表有相同的结点。时间复杂度为为线性:O(Length(h1) + Length(h2)),同时为了存储第一个链表的所有节点,空间复杂度为O(Length(h1))。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?
  3. 进一步考虑“如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。那么,我们只要判断俩个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交。
    所以,先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法时间复杂度为线性O(N),空间复杂度为O(1),显然比解法三更胜一筹。
  4. 上面的问题都是针对链表无环的,那么如果现在,链表是有环的呢?还能找到最后一个结点进行判断么?上面的方法还同样有效么?显然,这个问题的本质已经转化为判断链表是否有环。那么,如何来判断链表是否有环呢?

总结:
所以,事实上,这个判断两个链表是否相交的问题就转化成了:
1.先判断带不带环
2.如果都不带环,就判断尾节点是否相等
3.如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上。
如果在,则相交,如果不在,则不相交。

下面先给出是否有环的算法,就使用前面我的文章中的方法,但是题目的需要,增加了保存了尾节点。

算法和July的方法是一样的,但是实现的方式有点不同而已。

//判断链表是否有环
bool IsLoop(node *head, node **firstLoop, node **lastNode)
{
	node *first = head;
	node *second = head;

	if(NULL == head || NULL == head->next)
	{
		return false;
	}

	do
	{
		first = first->next;			//first走一步
		second = second->next->next;	//second走两步
	}while(second && second->next && first != second);

	if(first == second)
	{
		*firstLoop = first;		//保存环的首节点
		return true;
	}
	else
	{
		*lastNode = first->next;	//保存尾节点
		//printf("%d\n", first->next->data);
		return false;
	}
	return false;
}

接下来是判断是否相交的算法,和July的代码是一样的,只是自己写了一遍。学习了.....

//判断两链表是否相交
//1.如果都不带环,就判断尾节点是否相等  
//2.如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上。  
bool IsCross(node *head1, node *head2)
{
	node *firstLoop1 = NULL;
	node *firstLoop2 = NULL;
	node *lastNode1 = NULL;
	node *lastNode2 = NULL;

	if(NULL == head1 || NULL == head2)
	{
		printf("Link is Empty!\n");
		exit(1);
	}
	bool isLoop1 = IsLoop(head1, &firstLoop1, &lastNode1);
	bool isLoop2 = IsLoop(head2, &firstLoop2, &lastNode2);

	if(!isLoop1 && !isLoop2)	//两个都无环
	{
		return (lastNode1 == lastNode2);
	}

	else if(isLoop1 != isLoop2)	//一个有环,另一个无环
	{	
		return false;
	}
	else	//两个都有环,判断环里的节点是否能到达另一个链表环里的节点
	{
		node *temp = firstLoop1->next;
		while(temp != firstLoop1)
		{
			if(temp == firstLoop2)	//判断第二个链表里的环节点是否在第一个链表中
				return true;
			temp = temp->next;
		}
	}
	return false;
}

学习到了新东西,感谢July的经典文章。。






抱歉!评论已关闭.