链表篇五
链表篇(五)
链表相交
面试题 02.07. 链表相交
思路
在做这道题时,就非常容易陷入一个误区,我在初次思考时当成了顺序遍历后按照对应长度来判断数值是否相等来发现相交点,但会出现案例错误,
求两个链表交点节点的指针,目的是指针相等
只是题目简便化——假设节点元素数值相等,则节点指针相等。
因此我们采取双指针法——这里的案例运用了代码随想录的案例
假设节点元素数值相等,则节点指针相等。
求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
否则循环退出返回空指针。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while(curA != NULL){
lenA++;
curA = curA -> next;
}
while(curB != NULL){
lenB++;
curB = curB -> next;
}
curA = headA;
curB = headB;
if(lenB > lenA){
swap(lenA, lenB);
swap(curA, curB);
}
int del_len = lenA - lenB;
while(del_len --){
curA = curA -> next;
}
while(curA != NULL){
if(curA == curB){
return curA;
}
curA = curA -> next;
curB = curB -> next;
}
return NULL;
}
};
环形链表 ||
142. 环形链表 II
思路(妙计)
首先是对这道题的分解——
本题涉及到两个问题:
- 判断该链表内是否有环
- 环的入口在哪里
初次遇到这题,我们可以根据双指针法知道链表有无环的判断,但找到环的入口就需要一些数学推导
依次解决
判断链表是否有环
思路:使用快慢指针法,创建两个指针(fast和slow指针),先使两个指针都从头节点出发,fast指针每次移动两格,slow指针每次移动一格,若当fast指针和slow指针相遇时,则链表有环。
基于这种思路:有如下解释——(摘自代码随想录)
首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇。
那么来看一下,为什么fast指针和slow指针一定会相遇呢?
可以画一个环,然后让 fast指针在任意一个节点开始追赶slow指针。
会发现最终都是这种情况, 如下图
此时fast与slow都各自再走一步就相遇了,所以对于slow来说fast指针是一个节点接一个节点的接近slow的,所以两个指针一定可以重合。
找到环的入口
此处涉及数学推演
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示
由此可知,在相遇时,slow指针走过的节点数为:
x + y
, fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y):
x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以要求x ,将x单独放在左面:
x = n (y + z) - y
,再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:
x = (n - 1) (y + z) + z
注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针
由此以该公式可知:当n = 1时,即fast指针在环内绕一圈就可以遇到slow指针——n = 1时,x = z;
从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点
也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。
让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast -> next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
ListNode* index_1 = fast;
ListNode* index_2 = head;
while(index_1 != index_2){
index_1 = index_1->next;
index_2 = index_2->next;
}
return index_2;
}
}
return NULL;
}
};