今天看了July的一篇经典文章《程序员编程艺术:第九章、闲话链表追赶问题》,因为现在一直复习数据结构有关链表的算法,顺便总结下,学习下July大牛的判断链表是否相交。出处:http://blog.csdn.net/v_JULY_v 。
题目:给出两个单向链表的头指针,判断是否相交。
下面是July文章上面的解法:
分析:这是来自编程之美上的微软亚院的一道面试题目。请跟着我的思路步步深入(部分文字引自编程之美):
- 直接循环判断第一个链表的每个节点是否在第二个链表中。但,这种方法的时间复杂度为O(Length(h1) * Length(h2))。显然,我们得找到一种更为有效的方法,至少不能是O(N^2)的复杂度。
- 针对第一个链表直接构造hash表,然后查询hash表,判断第二个链表的每个结点是否在hash表出现,如果所有的第二个链表的结点都能在hash表中找到,即说明第二个链表与第一个链表有相同的结点。时间复杂度为为线性:O(Length(h1) + Length(h2)),同时为了存储第一个链表的所有节点,空间复杂度为O(Length(h1))。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?
- 进一步考虑“如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。那么,我们只要判断俩个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交。
所以,先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法时间复杂度为线性O(N),空间复杂度为O(1),显然比解法三更胜一筹。 - 上面的问题都是针对链表无环的,那么如果现在,链表是有环的呢?还能找到最后一个结点进行判断么?上面的方法还同样有效么?显然,这个问题的本质已经转化为判断链表是否有环。那么,如何来判断链表是否有环呢?
总结:
所以,事实上,这个判断两个链表是否相交的问题就转化成了:
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的经典文章。。