判断两条链表是否相交&单链表是否有环

一、判断两条链表是否相交

http://hi.baidu.com/gmfoqlmthriprur/item/fb96f7c8ca4db72fef46657f

相交的话,一定是Y型或者V型的。所以只需要判断两个链表最后一个节点就可以了。

问题描述:

给定两个单向列表的头指针,比如h1、h2,判断这两个列表是否相关。

注,这里只讨论这两个列表均不带环的情况。

解法一:直观的想法

估计有些人一看到这个题目,便不管三七二十一,从第一个链表开始遍历,判断它的每一个节点是否在第二个链表中。这是最简单的方法,但是时间复杂度却是O(length(h1)*length(h2)),非常耗时间。

解法二:

由于两个列表都没有环,那么我们可以把第二条链表接到第一条链表后面,如果得到的链表有环,则说明这两条链表有相交,如果没有环,则说明这两条链表没相交。判断一个链表是否有环,也不是一件简单的事,但是这里我们可以发现,假如有环,则第二条链表的表头一定在环上,因此我们只需要从第二条链表开始遍历,看是否会回到起始点就可以判断出来,当然了,最后不要忘了恢复原来的状态,去掉第一个链表到第二个链表表头的指向。

解法三:

上面的解法二的解法比起解法一来说明显高效了很多,但是要把两条链表拼起来,最后还要恢复它的状态,不免太麻烦了一点,那么有没有更好的办法呢?我们知道假如两条链表相交于某节点上,那么从该节点开始,两条链表共用同一个结点,也就是说最后一个节点肯定是共用的,这样我们便可以通过判断最后一个结点是否为两条链表共用来判断两条链表是否相交。

首先先遍历第一条链表,记住最后一个节点,接着遍历第二条链表,到最后一个节点时与第一条链表的最后一个节点相比较,如果两个节点相同,则两条链表相交,否则为不相交。该算法的时间复杂度为O(length(h1)+length(h2)),而且只用了一个额外的指针来保存最后一个节点,无疑地,这个方法比解法二更优。

扩展问题 如果两个链表相交,找出相交的第一个节点?
在判断相交的过程中要分别遍历两个链表,同时记下各自的长度。然后再遍历一次:长链表节点先从头节点出发前进(lengthMax-lenghMin)步,之后两个链表同时前进,每次一步,相遇的第一个节点即为两个链表相交的第一个节点。程序描述如下:
Node *intersection(Node *head1, Node *head2)
if(!head1 || !head2)
   return NULL;
int len1 = 1;
int len2 = 1;
bool result = false;
//判断两个链表是否相交,同时记下各个链表的长度
Node *p = head1;
while(p->next)
   pLen++; p=p->next
q=head2
while(q->next)
   len2++; q=q->next
result=(p==q)
if(result)
   int steps = abs(len1 – len2)
   Node *head = len1 > len2 ? head1 : head2;
   while(steps)
    head = head->next
    steps –-
   len1 > len2 ? p = head,q=head2 ? q = head,p=head1
   while(p!=q)
    p=p->next
    q=q->next
   return p
return NULL

 二、链表中含有环
链表中有环如下图:

1.链表中是否有环的判断
可以设置两个指针(fast,slow),初始值均指向头,slow每次向前一步,fast每次向前两步;
如果链表中有环,则fast先进入环中,而slow后进入环中,两个指针在环中必定相遇;
如果fast遍历到尾部为NULL,则无环
2.链表有环,判断环的入口点
  当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:

2s = s + nr
s= nr

设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)

(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点
 
因而,可以在链表头,相遇点分别设定一个指针,每次各走一步,两个指针必定相遇,则相遇第一点为环入口点
LinkList *  ListLoop(LinkList *list) //判断一个链表中是否有环
{
 int isLoop=0;
 LinkList *fast,*slow;
 fast=list;
 slow=list;
 while(fast&&fast->next)
 {
  slow=slow->next;
  fast=fast->next->next;//fast每次两步,slow每次一步
  if(fast==slow) //当两指针相等时,判断链表中有环
  {
   isLoop=1;
   break;
  }
 }
  if(isLoop==1)//链表中有环时
  {
   slow=list;
 while(slow!=fast)//一个头指针,一个相遇点指针,两个第一次相等时为环入口点
 {
  slow=slow->next;
  fast=fast->next;
 }
 return slow;
  }
  else
  {
   cout<<"链表中没有环";
   return NULL;
  }
当两个链表中有环时,相交的判断:
(1)首先分别找出两个链表入环的第一个结点记为p1,p2
 (2)如果p1==p2,说明两个链表在入环之前或入环的第一个结点相交;则此 时可以转为两个链表均不带环相交的判断,把p1,p2当作最后的末尾结点
(3)如果p1!=p2,此时两个链表可能完全不相交;也可能两个链表完全共有同一个环。
此时,判断 p1->next与p2->next是否相等,如果相等则两链表完全共环;如果不相等,则完全不相交
 
当一个链表中有环,一个链表中没有环时,两个链表必不相交。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值