算法学习之数据结构之链表是不是相交,链表是否存在环
算法学习之数据结构之链表是否相交,链表是否存在环
当看到判断两链表是否相交,判断链表是否存在环时,就感觉不知道从何下手,原因是不知道什么是链表相交,什么是链表存在环,所以当明白概念的时候,发现这两个问题并不难,而且,其实两个单链表是否相交是和链表中存在环是有关系的。
分析的图:

当看到判断两链表是否相交,判断链表是否存在环时,就感觉不知道从何下手,原因是不知道什么是链表相交,什么是链表存在环,所以当明白概念的时候,发现这两个问题并不难,而且,其实两个单链表是否相交是和链表中存在环是有关系的。
一、判断链表是否存在环。
一个链表存在环,指的是,某个节点的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; }
分析的图: