例题描述
编写一个程序,找到两个单链表相交的起始结点。
如下面的两个链表,在结点 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
个节点。
示例 3:
- 输入:intersectVal =
0
, listA =[2,6,4]
, listB =[1,5]
, skipA =3
, skipB =2
- 输出:
NULL
- 输入解释:从各自的表头开始算起,链表 A 为
[2,6,4]
,链表 B 为[1,5]
。由于这两个链表不相交,所以intersectVal
必须为0
,而skipA
和skipB
可以是任意值。
解释:这两个链表不相交,因此返回NULL
。
思路一
获取两链表的长度,让长链表先步进长度差个单位,然后共同步进相同步距,两指针相等(即指向地址相同)时找到第一次也是最后一次相交的结点,返回该结点。找不到,返回空。
- 处理极端情况,当两链表其中任一为空,直接返回空,因为空链表不可能相交。
- 创建两个指针
pLA
和pLB
分别指向两链表的头结点,通过这两个指针的变量与计数器的搭配使用,获取两链表的整表长度。
【此时gap = 5 - 6 = -1
】
- 创建一个临时变量
gap
表示两链表的长度的差值,因为这个值有可能为正,即第一条链表长,也有可能为负,所以需要分类讨论,通过if...else..
语句进行分类处理。 - 通过
while
循环先让长的那条链表先步进gap
个单位,这个不难实现,可以让这个gap
值分情况递增或者递减直至0
时退出循环即可。
【此时headB
步进了|gap|
步,gap
自身变为0
,退出了循环】
- 长链表步进完毕后,与短链表共同步进,步距相同,判断每次步进之后的两指针是否相等,即指向的内存空间地址是否相等,如果找到了,说明找到了两链表相交的起始结点。
【此时结点8
即为起始相交结点】
- 注意:这里有种特殊情况:长链表步进
gap
步后可能刚好位于相交结点处,所以条件判断语句一定要置于步进语句的前面,否则就无法正确找到首次相交的起始结点了。
代码实现
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (!headA || !headB) {
return NULL;
}
ListNode *pLA = headA;
ListNode *pLB = headB;
//1. 统计两链表长度
int sizeA = 0;
int sizeB = 0;
while (pLA) {
sizeA++;
pLA = pLA->next;
}
while (pLB) {
sizeB++;
pLB = pLB->next;
}
int gap = sizeA - sizeB;
pLA = headA;
pLB = headB;
//2. 让长链表先向后移动 gap 步
if (gap > 0) {
while (gap--) {
pLA = pLA->next;
}
}
else {
while (gap++) {
pLB = pLB->next;
}
}
//让pLA与pLB同时向后移动,并检测二者是否相等
//如果相等,说明该结点就是交点
while (pLA && pLB) {
if (pLA == pLB) {
return pLA;
}
pLA = pLA->next;
pLB = pLB->next;
}
return NULL;
}
思路二:简洁版
这个思路不去从头思考,而是从结果倒推。代码更简洁。
我们这么思考,如果题意是相交了,那么跑两趟的距离一定是相等的。
比如我从北京出发,小明从三亚出发,目的地是西安。那么我北京->西安->三亚的距离一定等于小明三亚->西安->北京的距离。
由此可以引出这个思路,如果一条链表走到头了,让他从另一条链表的头部开始走,那么他两个一定会殊途同归,相遇的那个点,就是两单链表遇见的节点,即起始节点。
(但有个前提,一定是要存在相遇交点,否则此思路无效)
[图源牛客网Dylan]
代码实现
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if (!pHead1 || !pHead2) return nullptr;
ListNode *p1 = pHead1, *p2 = pHead2;
while(p1 != p2) {
p1 = (p1 ? p1->next : pHead2);
p2 = (p2 ? p2->next : pHead1);
}
return p2;
}