判断俩链表是否相交

http://blog.csdn.net/lihappy999/article/details/7330175

给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。

为了简化问题,我们假设俩个链表均不带环。
问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?

建立hash表:
由于节点地址指针就是一个整型,假设链表都是在堆中动态创建的,可以使用堆的起始地址作为偏移量,以地址减去这个偏移量作为Hash函数。

a、都无环。b、一个有环一个无环。c、都有环
b肯定不相交,故只需要讨论a与c:若相交,则求交点。

环相交情况分为:环入口点不同、环入口点相同


上面第一种情况那个是第一个交点呢?

  当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)循环内环+相遇点到环入口点
寻找环点的方法如下:
两个指针,一个走一步,一个走两步,在环中相遇位置为X。然后从头节点和X位置,分别一步一步的走,每次判断是否相遇,相遇点就是所求 环入口点

当两个链表中有环时,相交的判断:
(1)首先分别找出两个链表入环的第一个结点记为p1,p2
(2)如果p1==p2,说明两个链表在入环之前或入环的第一个结点相交;则此时可以转为两个链表均不带环相交的判断,把p1,p2当作最后的末尾结点
(3)如果p1!=p2,此时两个链表可能完全不相交;也可能两个链表完全共有同一个环。
当一个链表中有环,一个链表中没有环时,两个链表必不相交。


一、判断是否带环
二、如果都不带环,就判断尾节点是否相等,记录长度,找交点
三、一个带环、一个不带环,则无交点
四、都带环,获得其中一环入口点r1,然后将环断开,判断另链表是否否是环,若不是环,说明两环相交。此时变成了两个无环的链表求相交点。否则,两环不相交。



//coder:LEE
//20120307
#include<iostream>
#include<cassert>
using namespace std;
struct List
{
int data;
List *next;
};
List *IsIntersect(List *h1,List *h2)
{//找两链表最后节点,若相等则链表相交,最后返回交点的指针
assert(h1!=NULL&&h2!=NULL);
int n1=1;
List *p1=h1;
List *p2=h2;
while(p1->next!=NULL)
{
p1=p1->next;
n1++;
}
int n2=1;
while(p2->next!=NULL)
{
p2=p2->next;
n2++;
}
if(p1!=p2)
return NULL;
int diff;
if(n1>n2)
{
diff=n1-n2;
while(diff--)
h1=h1->next;
}
else
{
diff=n2-n1;
while(diff--)
h2=h2->next;
}
while(h1!=h2)
{
h1=h1->next;
h2=h2->next;
}
return h1;
}
List *IsLoop(List *h1)
{
assert(h1!=NULL);
List *pFast=h1;
List *pSlow=h1;
while (pFast&&pFast->next)
{
pSlow=pSlow->next;
pFast=pFast->next->next;
if (pSlow==pFast)
{
while(h1!=pFast)//找到入口点
{
h1=h1->next;
pFast=pFast->next;
}
return h1;
}
}
return NULL;
}
void Init(List *&p,int starti)
{
List *pCur;
for (int i=starti;i<starti+7;i++)
{
List *pNext=new List();
pNext->data=i;
pNext->next=NULL;
if(i==starti)
{
p=pNext;
pCur=p;
}
else
{
pCur->next=pNext;
pCur=pNext;
}
}
}
void Print(List *p)
{int k=0;
while(p)
{k++;
cout<<p->data<<" ";
p=p->next;
if(k>20)//打印环的部分
break;
}
cout<<endl;
}
List *FindPoint(List *h1,List *h2)
{
assert(h1!=NULL&&h2!=NULL);
List *p1=IsLoop(h1);
List *p2=IsLoop(h2);
if(p1==NULL&&p2==NULL)//都不带环
return IsIntersect(h1,h2);
else if (p1!=NULL && p2!=NULL)//都带环
{
List *pLast=p1;
while (pLast->next!=p1)
pLast=pLast->next;
pLast->next=NULL;//找到入口点前一点,然后将环切断
List *p2=IsLoop(h2);
//若切断环使另一链表也成为了无环的,说明两环有交点,
//则此时可以转为两个链表均不带环相交的判断。
if(p2)
return NULL;
else
return IsIntersect(h1,h2);//当有两个环入口点时,哪个才是第一个交点呢?
}
else
return NULL;
}
int main()
{
List *head1;
Init(head1,1);
List *head2;
Init(head2,9);
List *p2=head2;
while(p2->next!=NULL)
p2=p2->next;
p2->next=head1->next->next;//将15后链接3
List *p1=head1;
while(p1->next!=NULL)
p1=p1->next;
p1->next=head2->next;//将7后链接10,形成环
    Print(head1);
Print(head2);

List *point=FindPoint(head1,head2);//找交点
if(point)
cout<<point->data<<endl;
else
cout<<"no point of intersect !"<<endl;


return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值