160. 相交链表 ——【Leetcode每日一题】
160. 相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构
自定义评测:
评测系统
的输入如下(你设计的程序 不适用 此输入):
intersectVal
- 相交的起始节点的值。如果不存在相交节点,这一值为 0listA
- 第一个链表listB
- 第二个链表skipA
- 在listA
中(从头节点开始)跳到交叉节点的节点数skipB
- 在listB
中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA
和 headB
传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出: Intersected at ‘8’
解释: 相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。
示例 2:
输入: intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出: Intersected at ‘2’
解释: 相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,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 。
提示:
listA
中节点数目为m
listB
中节点数目为n
- 1 < = m , n < = 3 ∗ 1 0 4 1 <= m, n <= 3 * 10^4 1<=m,n<=3∗104
- 1 < = N o d e . v a l < = 1 0 5 1 <= Node.val <= 10^5 1<=Node.val<=105
- 0 <= skipA <= m
- 0 <= skipB <= n
- 如果
listA
和listB
没有交点,intersectVal
为 0 - 如果
listA
和listB
有交点,intersectVal
==listA[skipA]
== `listB[skipB]``
进阶: 你能否设计一个时间复杂度 O ( m + n ) O(m + n) O(m+n) 、仅用 O ( 1 ) O(1) O(1) 内存的解决方案?
思路:
例如以下示例中 A
和 B
两个链表相交于 c1
:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
但是不会出现以下相交的情况,因为每个节点只有一个 next
指针,也就只能有一个后继节点,而以下示例中节点 c
有两个后继节点。
A: a1 → a2 d1 → d2
↘ ↗
c
↗ ↘
B: b1 → b2 → b3 e1 → e2
要求时间复杂度为
O
(
N
)
O(N)
O(N),空间复杂度为
O
(
1
)
O(1)
O(1)。如果不存在交点则返回 null
。
设 A
的长度为 a + c
,B 的长度为 b + c
,其中 c
为尾部公共部分长度,可知 a + c + b = b + c + a
。
当访问 A
链表的指针访问到链表尾部时,令它从链表 B
的头部开始访问链表 B
;同样地,当访问 B
链表的指针访问到链表尾部时,令它从链表 A
的头部开始访问链表 A
。这样就能控制访问 A
和 B
两个链表的指针能同时访问到交点。
如果不存在交点,那么 a + b = b + a
,以下实现代码中 l1
和 l2
会同时为 null
,从而退出循环。
代码:(Java、C++)
Java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode l1 = headA, l2 = headB;
while (l1 != l2) {
l1 = (l1 == null) ? headB : l1.next;
l2 = (l2 == null) ? headA : l2.next;
}
return l1;
}
}
C++
/**
* 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* l1 = headA;
ListNode* l2 = headB;
while (l1 != l2) {
l1 = (l1 == NULL) ? headB : l1->next;
l2 = (l2 == NULL) ? headA : l2->next;
}
return l1;
}
};
运行结果:
复杂度分析:
-
时间复杂度:时间复杂度: O ( m + n ) O(m+n) O(m+n),其中 m m m 和 n n n 是分别是链表 h e a d A headA headA 和 h e a d B headB headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。
-
空间复杂度: O ( 1 ) O(1) O(1)。
题目来源:力扣。
放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我 leetCode专栏,每日更新!