两个链表的第一个公共节点

JZ52两个链表的第一个公共节点

描述

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围: $n≤1000n≤1000$
要求:空间复杂度 $O(1)$,时间复杂度 $O(n)$

例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:img

可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。

输入描述:

输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数$FindFirstCommonNode$里面,用户得到的输入只有$pHead1$和$pHead2$。

返回值描述:

返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。

示例1

1
2
3
4
输入:{1,2,3},{4,5},{6,7}
返回值:{6,7}
说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的

示例2

1
2
3
输入:{1},{2,3},{}
返回值:{}
说明:2个链表没有公共节点 ,返回null,后台打印{}

题解

首先分析题目,这道题目的题干有些复杂,但是也暗示了一些信息,主要来自于输入描述部分:输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。由此我们可以得到这道题的重要解题思路:公共部分一定处于两个链表共同的尾部,以及相同部分的指针是相同的

我最初的想法是将两个链表全部反转,然后从尾到头遍历,找到第一个不同的之后自然就能够定位最后一个相同的(反转前的第一个相同的),再把链表反转回去即可。但是好像因为这道题的后台输入问题导致这个方法不能AC。

看一下题解可以知道把我的方法反向思考即可,首先遍历两个链表,获得二者长度差,让长的那个的头先走长度差个距离,此时二者剩余长度相同,双指针同时遍历,找到相同节点即可。

刚才想着快慢指针也能做这个题,但是仔细一想快慢指针不能确定找到的相同处就一定是第一个相交点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if (!pHead1||!pHead2) {
return nullptr;
}
ListNode* head1 = pHead1;
int size1=0;
while(head1){
head1 = head1->next;
size1+=1;
}
ListNode* head2 = pHead2;
int size2 = 0;
while(head2){
head2 = head2->next;
size2+=1;
}
head1 = pHead1;
head2 = pHead2;
if(size1 > size2){
for(int i=0;i<size1-size2;i++)
head1 = head1->next;
}else{
for(int i=0;i<size2-size1;i++)
head2 = head2->next;
}

while(head1 && head2){
if(head1 == head2)
return head1;
head1 = head1->next;
head2 = head2->next;
}
return nullptr;
}
};