当看到判断两链表是否相交,判断链表是否存在环时,就感觉不知道从何下手,原因是不知道什么是链表相交,什么是链表存在环,所以当明白概念的时候,发现这两个问题并不难,而且,其实两个单链表是否相交是和链表中存在环是有关系的。
分析的图:
一、判断链表是否存在环。
一个链表存在环,指的是,某个节点的next指向的是链表中在他之前的节点,这样在链表尾部形成环。(这个概念很重要。)
弄明白概念后,对于下面两个问题就比较好解决了。
问题1,如何判断链表中存在环?
问题2如果链表存在环,如何找到环的入口点?
问题1解决办法,弄懂环的概念以后,就能轻易解决了。设置两个指针(slow,fast),初始值都只想头结点,然后slow每次前进一步,fast每次前进两步,如果链表存在环,则fast先进入环,slow后进入环,最后fast会赶上slow两个指针会相遇,如果不存在环,则fast到尾节点则为null了,这样就不会相遇了。具体代码如下:
bool IsExitsLoop(slist *head)
slist *slow, *fast;
slow = fas t= head;
while(fast != null && fast->next != null){
slow=slow->next;
fast=fast->next->next;
if(fast == slow){
break;
}
}
return (fast == null || fast->next == null) ? false : true;
问题2解决办法,碰撞点p到连接点的距离=头指针到连接点的距离,则设
两个指针分别从头指针开始走和从相遇点开始走,这样他们会在环入口点相遇
。解释如下:
当fast遇到slow时,slow还没走到尾节点,fast在环内走了n圈,设slow走了s个节点,则fast走了2s个节点,设环长为r则,fast走的距离等于slow走的距离加上在环内走的距离(nr
),则有
2s=s+nr,则s=nr。
设链表长为L,头结点到环入口点的距离为a,环入口点到相遇点的距离为x,则有s=a+x, s=nr, a+x=nr, a+x=(n-1)r+r, r=L-a, a+x=(n-1)r+L-a, a=(n-1)r+L-a-x,a为起点到环入口点的距离,等于n-1个环的距离加上L-a-x为相遇点到换入口点的距离。于是设
两个指针分别从头指针开始走和从相遇点开始走,这样他们会在环入口点相遇
,则再次相遇的点为环的入口点。具体分析过程看后面的手绘图。
具体代码如下:
slist* FindLoopPort(slist *head)
{
slist *slow = head, *fast = head;
while ( fast && fast->next )
{
slow = slow->next;
fast = fast->next->next;
if ( slow == fast ) break;
}
// 如果没有环则返回空
if (fast == NULL || fast->next == NULL)
return NULL;
// 分别从头结点和相遇点开始走
slow = head;
// 两个节点一直走直到相遇
while (slow != fast)
{
slow = slow->next;
fast = fast->next;
}
// 返回相遇点
return slow;
}
二、判断两个单链表是否相交。
方法一,其实这个和链表中是否有环是有关联的。两个单链表相交是Y型相交,而不是X型相交。所以两个链表相交,把其中一个链表的头结点接在另一个链表的尾节点后面,如果两链表相交,则连接后形成一个环。否则不形成环。具体分析过程看后面的手绘图。所以判断方法和判断链表中是否存在环的方法一样,前面加一步把两个链表连接就行了。
方法二,因为是Y型相交,则从如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,这样就可以采用以下办法。我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。
接下来找出相交点。这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。具体代码如下:
Node *intersection(Node *head1, Node *head2)
if(!head1 || !head2)
return NULL;
int len1 = 1, len2 = 1;
Node *p = head1, *q=head2, *fast, *slow;
bool result = false;
//判断两个链表是否相交,同时记下各个链表的长度
while(p->next){
len1++;
p=p->next;
}
while(q->next){
len2++;
q=q->next;
}
// 尾节点相同则为相交,否则不想交
result=(p==q);
if(!result){
return NULL; // 不存在则返回NULL
}else{
// 链表长的先走lengthMax-lengthMin步
int steps = abs(len1 – len2);
if(len1 > len2){
fast = head1;
slow = head2;
}else{
fast = head2;
slow = head1;
}
while(steps){
fast = fast->next;
steps –-;
}
// 之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
分析的图: