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

 今天看了 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的方法是一样的,但是实现的方式有点不同而已。

//判断链表是否有环   

  1. bool IsLoop(node *head, node **firstLoop, node **lastNode)  
  2. {  
  3.     node *first = head;  
  4.     node *second = head;  
  5.   
  6.     if(NULL == head || NULL == head->next)  
  7.     {  
  8.         return false;  
  9.     }  
  10.   
  11.     do  
  12.     {  
  13.         first = first->next;         //first走一步   
  14.         second = second->next->next;  //second走两步   
  15.     }while(second && second->next && first != second);  
  16.   
  17.     if(first == second)  
  18.     {  
  19.         *firstLoop = first;     //保存环的首节点   
  20.         return true;  
  21.     }  
  22.     else  
  23.     {  
  24.         *lastNode = first->next; //保存尾节点   
  25.         //printf("%d\n", first->next->data);   
  26.         return false;  
  27.     }  
  28.     return false;  
接下来是判断是否相交的算法,和July的代码是一样的,只是自己写了一遍。学习了.....

//判断两链表是否相交   

  1. //1.如果都不带环,就判断尾节点是否相等     
  2. //2.如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上。     
  3. bool IsCross(node *head1, node *head2)  
  4. {  
  5.     node *firstLoop1 = NULL;  
  6.     node *firstLoop2 = NULL;  
  7.     node *lastNode1 = NULL;  
  8.     node *lastNode2 = NULL;  
  9.   
  10.     if(NULL == head1 || NULL == head2)  
  11.     {  
  12.         printf("Link is Empty!\n");  
  13.         exit(1);  
  14.     }  
  15.     bool isLoop1 = IsLoop(head1, &firstLoop1, &lastNode1);  
  16.     bool isLoop2 = IsLoop(head2, &firstLoop2, &lastNode2);  
  17.   
  18.     if(!isLoop1 && !isLoop2)    //两个都无环   
  19.     {  
  20.         return (lastNode1 == lastNode2);  
  21.     }  
  22.   
  23.     else if(isLoop1 != isLoop2) //一个有环,另一个无环   
  24.     {     
  25.         return false;  
  26.     }  
  27.     else    //两个都有环,判断环里的节点是否能到达另一个链表环里的节点   
  28.     {  
  29.         node *temp = firstLoop1->next;  
  30.         while(temp != firstLoop1)  
  31.         {  
  32.             if(temp == firstLoop2)  //判断第二个链表里的环节点是否在第一个链表中   
  33.                 return true;  
  34.             temp = temp->next;  
  35.         }  
  36.     }  
  37.     return false;  
  38. }  
学习到了新东西,感谢July的经典文章。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值