转自:http://blog.csdn.net/zcsylj/article/details/6800052
给定两个链表,如何判断两个链表相交,若相交求出交点。
首先应该分两种情况考虑,第一:这两个链表本身都没有环。 第二:这两个链表本身都有环
首先讲都没有环的情况,该情况如下面转载的问题3
然后说第二种情况,两个链表本身都有环,首先我们应该知道如果一个链表有环,一个链表没有环,那么他们肯定不想交,自己画画图就明白了,不多说,若两个链表都有环,然后求出了各自的入口点后,这时候又分了两种情况,入口点相同或者不同,如下图:
那这样就很那说哪个是交点了,因为有两个交点,其实不叫两个交点,是相对于l1来讲交点是l2的环点,相对于l2来讲交点是l1的环点而环点同样是前面的方法得到的。
- template<typename T>
- ListNode<T>* FindFirstCommonNode_2(ListNode<T>* pHead1, ListNode<T>* pHead2)
- {
- ListNode<T>* pEntrance1 = FindRingEntrance(pHead1);
- ListNode<T>* pEntrance2 = FindRingEntrance(pHead2);
- ListNode<T>* pResult = NULL;
- if(pEntrance1 == pEntrance2)
- {
- ListNode<T>* pTemp1 = NULL, *pTemp2 = NULL;
- if(pEntrance1)
- {
- // 环入口点已经相交,寻找第一交点。
- pTemp1 = pEntrance1->pNext;
- pTemp2 = pEntrance2->pNext;
- pEntrance1->pNext = NULL;//断裂后变成两个单链表的方法来计算
- pEntrance2->pNext = NULL;
- pResult = FindFirstCommonNode(pHead1, pHead2);
- pEntrance1->pNext = pTemp1;
- pEntrance2->pNext = pTemp2;
- }
- else
- {
- // 无环单链表求第一交点
- pResult = FindFirstCommonNode(pHead1, pHead2);
- }
- return pResult;
- }
- else if(pEntrance1 == NULL || pEntrance2 == NULL)
- {
- // 一有环,另一个无环,无交点
- return NULL;
- }
- else
- {
- // 入口点不同的两环求交点
- pResult = pEntrance1->pNext;
- while(pResult != pEntrance2 && pResult != pEntrance1)
- {
- pResult = pResult->pNext;
- }
- return pResult == pEntrance2 ? pResult : NULL; //不确定两个环链表是否相交,否则两个环结点任一个都是结果
- }
- }
1、 给出两个单向链表的头指针pHead1和pHead2,判断这两个链表是否相交。假设两个链表均不带环。
示意图如下:
如果两个链表相交于某一节点,那么在这个相交节点之后的所有节点都是两个链表所共有的。也就是说,如果两个链表相交,那么最后一个节点肯定是共有的。先遍历第一个链表,记住最后一个节点,然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则不相交。时间复杂度为O( len1 + len2),因为只需要一个额外指针保存最后一个节点地址,空间复杂度为O(1)。(编程之美上面有详细的介绍)
2、给出一个单向链表的头指针pHead,判断链表中是否有环。
示意图如下:
链表中有环,其实也就是自相交。我们用两个指针pslow和pfast从头开始遍历链表,pslow每次前进一个节点,pfast每次前进两个结点,若存在环,则pslow和pfast肯定会在环中相遇,若不存在,则pslow和pfast能正常到达最后一个节点(实际上是到达NULL)。
代码如下:
- // 判断链表中是否有环
- bool IsExitLoop(LinkList *head)
- {
- LinkList *pslow = head;
- LinkList *pfast = head;
- while(pfast != NULL && pfast->next != NULL)
- {
- pslow = pslow->next; // 每次前进一步
- pfast = pfast->next->next; // 每次前进二步
- if(pslow == pfast) // 两个指针相遇,说明存在环
- return true;
- }
- return false; // 没有环
- }
方法一:
判断两个链表中是否存在地址一致的节点,就可以知道是否相交了。可以对第一个链表的节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果它在hash表中出现,则说明两个链表有共同的结点。这个方法的时间复杂度为:O(max(len1+len2);但同时还得增加O(len1)的存储空间存储哈希表。这样减少了时间复杂度,增加了存储空间。
以链表节点地址为值,遍历第一个链表,使用Hash保存所有节点地址值,结束条件为到最后一个节点(无环)或Hash中该地址值已经存在(有环)。
方法二:
对第一个链表遍历,计算长度len1,同时保存最后一个节点的地址。
对第二个链表遍历,计算长度len2,同时检查最后一个节点是否和第一个链表的最后一个节点相同,若不相同,则不相交,程序结束。
若相交,两个链表均从头节点开始,假设len1大于len2,那么将第一个链表先遍历len1-len2个节点,此时两个链表当前节点到第一个相交节点的距离就相等了,比较下一个节点是不是相同,如果相同就返回该节点(即相交节点),若不相同,两个链表都同步向后走一步,继续比较。
示意图如下:
![](https://img-my.csdn.net/uploads/201205/19/1337416881_3126.jpg)
方法三:
由于两个链表都没有环,我们可以把第二个链表接在第一个链表的后面,如果得到的链表有环,则说明这两个链表相交。否则,这两个链表不相交。这样我们就把问题转化为判断一个链表是否有环了。最后,当然可别忘记恢复原来的状态,去掉从第一个链表到第二个链表表头的指向。
4、给出一个单向链表的头指针pHead,判断链表中是否有环,若存在,则求出进入环中的第一个节点。
示意图如下:
![](https://img-my.csdn.net/uploads/201205/19/1337416898_6846.jpg)
红色虚线框中的节点为待求节点。
首先使用第2个题目中的快、慢指针来判断链表是否存在环,若不存在结束。
若链表中存在环,我们从链表头、与两个指针的相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇的第一个点为环的入口点。
代码如下:
- // 找到环的第一个入口点
- LinkList* FindLoopPort(LinkList *head)
- {
- LinkList *pslow = head;
- LinkList *pfast = head;
- while(pfast != NULL && pfast->next != NULL)
- {
- pslow = pslow->next; // 每次前进一步
- pfast = pfast->next->next; // 每次前进二步
- if(pslow == pfast) // 两个指针相遇,说明存在环
- break;
- }
- if(pfast == NULL || pfast->next == NULL) // 不存在环
- return NULL;
- pslow = head;
- while(pslow != pfast)
- {
- pslow = pslow->next; // 每次前进一步
- pfast = pfast->next; // 每次前进一步
- }
- return pslow; // 返回环的入口点
- }
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)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
小结:链表是数据结构中最基本的,也是面试中常考的,与链表相关的题目也变化多端,只要基础扎实,多积累一些处理类似问题的技巧,面试时便能应对自如。
单链表的的归并排序,同样需要找到链表的中间节点,可以使用前面的这个快、慢指针的方法。
- typedef struct LNode
- {
- int data;
- struct LNode *next;
- }LNode , *LinkList;
- // 对两个有序的链表进行递归的归并
- LinkList MergeList_recursive(LinkList head1 , LinkList head2)
- {
- LinkList result;
- if(head1 == NULL)
- return head2;
- if(head2 == NULL)
- return head1;
- if(head1->data < head2->data)
- {
- result = head1;
- result->next = MergeList_recursive(head1->next , head2);
- }
- else
- {
- result = head2;
- result->next = MergeList_recursive(head1 , head2->next);
- }
- return result;
- }
- // 对两个有序的链表进行非递归的归并
- LinkList MergeList(LinkList head1 , LinkList head2)
- {
- LinkList head , result = NULL;
- if(head1 == NULL)
- return head2;
- if(head2 == NULL)
- return head1;
- while(head1 && head2)
- {
- if(head1->data < head2->data)
- {
- if(result == NULL)
- {
- head = result = head1;
- head1 = head1->next;
- }
- else
- {
- result->next = head1;
- result = head1;
- head1 = head1->next;
- }
- }
- else
- {
- if(result == NULL)
- {
- head = result = head2;
- head2 = head2->next;
- }
- else
- {
- result->next = head2;
- result = head2;
- head2 = head2->next;
- }
- }
- }
- if(head1)
- result->next = head1;
- if(head2)
- result->next = head2;
- return head;
- }
- // 归并排序,参数为要排序的链表的头结点,函数返回值为排序后的链表的头结点
- LinkList MergeSort(LinkList head)
- {
- if(head == NULL)
- return NULL;
- LinkList r_head , slow , fast;
- r_head = slow = fast = head;
- // 找链表中间节点的两种方法
- /*
- while(fast->next != NULL)
- {
- if(fast->next->next != NULL)
- {
- slow = slow->next;
- fast = fast->next->next;
- }
- else
- fast = fast->next;
- }*/
- while(fast->next != NULL && fast->next->next != NULL)
- {
- slow = slow->next;
- fast = fast->next->next;
- }
- if(slow->next == NULL) // 链表中只有一个节点
- return r_head;
- fast = slow->next;
- slow->next = NULL;
- slow = head;
- // 函数MergeList是对两个有序链表进行归并,返回值是归并后的链表的头结点
- //r_head = MergeList_recursive(MergeSort(slow) , MergeSort(fast));
- r_head = MergeList(MergeSort(slow) , MergeSort(fast));
- return r_head;