注:本文的代码实现使用的是 JS(JavaScript),为前端中想使用JS练习算法和数据结构的小伙伴提供解题思路。
描述
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。题目数据保证整个链式结构中不存在环。
图示两个链表在节点 c1 开始相交:
注意,函数返回结果后,链表必须保持其原始结构 。
示例:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
提示:
- listA 中节点数目为 m
- listB 中节点数目为 n
- 0 <= m, n <= 3 * 104
- 1 <= Node.val <= 105
- 0 <= skipA <= m
- 0 <= skipB <= n
- 如果 listA 和 listB 没有交点,intersectVal 为 0
- 如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]
解题思路
哈希法
创建一个哈希表,然后依次存放其中一个链表的所有节点。随后遍历另一个链表,遍历的时候判断当前节点是否在哈希表中出现过,若出现,则为交点,若遍历结束,则返回null
var getIntersectionNode = function(headA, headB) {
let setA = new Set()
let pA = headA
let pB = headB
// 将 A 链表中的每个节点都放到集合中
while(pA){
setA.add(pA)
pA = pA.next
}
// 检测 B 中的节点是否曾经在 A 中出现
while(pB){
if(setA.has(pB)) return pB
pB = pB.next
}
return null
};
双指针法
记A独有的节点数为a
,B独有的节点数为b
,交点及其之后的节点数为c
(即A和B共享的节点)。
我们可以使用两个指针,分别指向A和B,依次遍历,遍历完自己的节点后遍历对方的节点。若某个时刻两个指针所指向的节点相同,则存在相交节点。
举例:假设相交节点存在。初始指向链表A 的指针为pA,遍历完后,走了a+c
步,此时将pA指向链表B的头节点,若想pA走到交点,需要再走b
步,累计走过了a+c+b
步;同理,始指向链表B 的指针为pB,遍历完后,走了b+c
步,此时将pB指向链表A的头节点,若想pB走到交点,需要再走a
步,需要走过b+c+a
步,此时正好步数一样。
var getIntersectionNode = function(headA, headB) {
if(!headA || !headB) return null
let pA = headA, pB = headB
while(pA !== pB){
pA = pA === null ? headB : pA.next
pB = pB === null ? headA : pB.next
}
return pA
};