原题目链接
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。\
题目分析
这种题做过几个了类似的了,有几个常见的思路:直接求解(双循环遍历)、hash法(存储再检查)、双指针法。
直接求解时间复杂度O(N^2),而hash法空间复杂度O(N),相比之下双指针法复杂度更优秀。
解题分析
其实双指针有很多具体的实现,不同跳转规也不同,不能直接套用。
Q:如何定义查找成功?
A:需要同时走到一点且为可以指出相交节点(如果存在)
Q:如何同时走到?
A:链表一长度可表示为 a + c、链表二长度可表示为为 b + c(c为公共部分)
Q:在某种组合的情况下…
A:对,a + c + b = b + c + a,且下一节点就是相交节点。
代码实现
失败案例,频频报错,debug心态爆炸。贴上来作为警示:先想好再写?
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA || !headB)
return nullptr; // 特殊情况
ListNode *a = headA;
ListNode *b = headB;
int flag = 0;
int res = 1;
while(a -> next && b -> next)
{
a = a -> next;
b = b -> next;
}
if(!a -> next && !b -> next)
{
a = headB;
b = headA;
}
else if(a -> next)
{
b = headA;
flag = 1;
a -> next;
while(a -> next)
{
a = a -> next;
b = b -> next;
}
}
else
{
a = headB;
flag = 2;
b -> next;
while(b -> next)
{
b = b -> next;
}
}
if(flag == 1)
{
a = headB;
b = b -> next;
}
else
{
b = headA;
a = a -> next;
}
if(!a || !b)
{
return nullptr;
}
while(!a || !b )
{
if(a == b)
return a;
if(a == nullptr || b == nullptr)
{
res = 0;
break;
}
a = a -> next;
b = b -> next;
}
return nullptr;
}
};
把计数统计单独提出来,作为lenA 和 LenB,同时不再移到头结点同时一个个移动,直接移动头结点。核心是减少if…else 判断!!!
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA || !headB)
return nullptr;
ListNode *A = headA;
ListNode *B = headB;
int lenA = 0;
int lenB = 0;
while(A){
lenA ++;
A = A -> next;
}
while(B){
lenB ++;
B = B -> next;
}
int i = max(lenA,lenB) - min(lenA,lenB);
if(lenA > lenB)
for(i; i > 0; -- i)
headA = headA -> next;
else
for(i; i > 0; -- i)
headB = headB -> next;
while(headA)
{
if(headA == headB)
return headA;
headA = headA -> next;
headB = headB -> next;
}
return nullptr;
}
};
复杂度分析:
- 时间复杂度:O(m+n)
- 空间复杂度:O(1)
其他方式
基于三元运算符的极短实现,很巧妙。
Whyzzj 的回答
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
//假设A比B长
if(headA==NULL||headB==NULL)return NULL;
struct ListNode* pA=headA;
struct ListNode* pB=headB;
while(pA!=pB)//遍历两个链表
{
pA=pA==NULL?headB:pA->next;//构造链表D
pB=pB==NULL?headA:pB->next;//构造链表C
}
return pA;
}
};
写在最后
无话可说