剑指 Offer(第2版)面试题 52:两个链表的第一个公共结点
剑指 Offer(第2版)面试题 52:两个链表的第一个公共结点
解法1:计算链表长度
先计算出两个链表的长度,可以让比较长的先走两个链表长度之差的步数,两个再一起走。
即使两个链表没有公共部分,它们都是同时到达自己的链表尾,都为 nullptr,返回 p1,也就是 nullptr,符合题意。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution
{
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB)
{
if (headA == nullptr || headB == nullptr)
return nullptr;
int lenA = listLength(headA), lenB = listLength(headB), diff = abs(lenA - lenB);
ListNode *p = lenA > lenB ? headB : headA, *q = lenA > lenB ? headA : headB;
while (diff--)
q = q->next;
while (p != q)
{
p = p->next;
q = q->next;
}
return q;
}
// 辅函数 - 计算链表长度
int listLength(ListNode *head)
{
if (head == nullptr)
return 0;
int length = 0;
ListNode *p = head;
while (p)
{
p = p->next;
length++;
}
return length;
}
};
复杂度分析:
时间复杂度:O(lenA+lenB),其中 lenA 是链表 A 的长度, lenB 是链表 B 的长度。
空间复杂度:O(1)。
解法2:双指针
假设链表 A 的单独部分的长度为 a, 链表 B 的单独部分的长度为 b,两个链表的公共部分长度为 c。
以下图为例:

a = 2,b = 3,c = 3。
我们发现有:a + c + b = b + c + a。
于是我们让两个指针一起走,pa 走到头就转向 headB,然后继续走;pb 走到头转向headA,然后继续走。最后 pa 和 pb 在公共部分的第一个节点相遇。
即使两个链表没有公共部分,pa 和 pb 都是同时到达链表 A 和链表 B 的链表尾(a + b = b + a),都为 nullptr,返回 pa,也就是 nullptr,符合题意。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution
{
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB)
{
if (headA == nullptr || headB == nullptr)
return nullptr;
ListNode *p1 = headA, *p2 = headB;
while (p1 != p2)
{
if (p1)
p1 = p1->next;
else
p1 = headB;
if (p2)
p2 = p2->next;
else
p2 = headA;
}
return p1;
}
};
复杂度分析:
时间复杂度:O(lenA+lenB),其中 lenA 是链表 A 的长度, lenB 是链表 B 的长度。
空间复杂度:O(1)。
解法3:栈
分别把两个链表的节点放入两个栈中,这样两个链表的尾节点就位于两个栈的栈顶,接下来比较两个栈顶的节点是否相同。如果相同,则弹出,接着比较下一个栈顶,直到找到最后一个相同的节点。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
if(headA == nullptr || headB == nullptr)
return nullptr;
stack<ListNode *> stkA, stkB;
ListNode *pA = headA, *pB = headB;
while(pA)
{
stkA.push(pA);
pA = pA->next;
}
while(pB)
{
stkB.push(pB);
pB = pB->next;
}
ListNode *sameNode = nullptr;
while(!stkA.empty() && !stkB.empty())
{
if(stkA.top() == stkB.top())
{
sameNode = stkA.top();
stkA.pop();
stkB.pop();
}
else
break;
}
return sameNode;
}
};
复杂度分析:
时间复杂度:O(lenA+lenB),其中 lenA 是链表 A 的长度, lenB 是链表 B 的长度。我们对链表 A 和链表 B 各遍历一次,将其节点分别插入 stkA 和 stkB。
空间复杂度:O(lenA+lenB),其中 lenA 是链表 A 的长度, lenB 是链表 B 的长度。栈 stkA 和 stkB 的空间开销。
本文介绍了如何解决剑指Offer面试题52,即寻找两个链表的第一个公共结点。提供了三种方法:计算链表长度后让较长链表追赶,双指针遍历,以及使用栈存储节点并比较栈顶。所有方法的时间复杂度均为O(lenA+lenB),空间复杂度分别为O(1)和O(lenA+lenB)。
面试题 52:两个链表的第一个公共结点&spm=1001.2101.3001.5002&articleId=135043317&d=1&t=3&u=8d480efd044c465e92e2369580de71ef)
2018

被折叠的 条评论
为什么被折叠?



