【Day 10】 相交链表

题目描述

  • 编写一个程序,找到两个单链表相交的起始节点。
    如下面的两个链表:

image
在节点 c1 开始相交。
示例 1:

image
输入: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:

image
输入: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:

image
输入: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。
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的回答

https://github.com/leetcode-pp/91alg-1/issues/28#issuecomment-641730514

解法一

理解了好久 链表的 next 指向的是引用地址,只需要向每个地址中添加一个标记位,B 链表访问到的第一个有标记位的地址就是公共交点

时空复杂度 O(2n)

var getIntersectionNode = function (headA, headB) {
  while (headA) {
    headA.flag = true;
    headA = headA.next;
  }
  while (headB) {
    if (headB.flag) return headB;
    headB = headB.next;
  }
  return null;
};

解法二

时空复杂度 O(n)

这个解法看题解理解的,把两个链表连接起来,会得到两条数量相同,但是连接地址不同的链表,如果有相交节点就最后几位就会完全相同。

var getIntersectionNode = function (headA, headB) {
  let A = headA;
  B = headB;
  if (A === null || B === null) return null;
  while (A !== B) {
    A = A == null ? headB : A.next;
    B = B == null ? headA : B.next;
  }
  return A;
};

参考回答

哈希法:
有 A, B 这两条链表, 先遍历其中一个,比如 A 链表,
将 A 中的所有节点存入哈希表。
遍历 B 链表,检查节点是否在哈希表中,
第一个存在的就是相交节点
时间复杂度 O(N), 空间复杂度 O(N)

let data = new Set();
while (A !== null) {
  data.add(A);
  A = A.next;
}
while (B !== null) {
  if (data.has(B)) return B;
  B = B.next;
}
return null;

双指针:
使用 a, b 两个指针分别指向 A, B 这两条链表, 两个指针相同的速度向后移动,
当 a 到达链表的尾部时,重定位到链表 B 的头结点
当 b 到达链表的尾部时,重定位到链表 A 的头结点。
a, b 指针相遇的点为相交的起始节点,否则没有相交点
PS: 为什么 a, b 指针相遇的点一定是相交的起始节点? 我们证明一下:

如果我们将两条链表按相交的起始节点继续截断,

A 链表为: a + c,
B 链表为: b + c,
当 a 指针将 A 链表遍历完后,重定位到链表 B 的头结点,然后继续遍历至相交点,
a 指针遍历的距离为 a + c + b,
同理 b 指针遍历的距离为 b + c + a。
时间复杂度 O(N), 空间复杂度 O(1)

let a = headA, b = headB
while(a != b){
a = a ? a.next : null
b = b ? b.next : null
if(a == null && b == null) return null
if(a == null) a = headB
if(b == null) b = headA
}
return a

作者:ZStar01
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/shuang-zhi-zhen-ha-xi-by-zstar01/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。