ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Golang leetcode160相交链表 map 双指针迭代

2026/8/16 16:43:19 拓冰建站 浏览量
Golang leetcode160相交链表 map 双指针迭代

文章目录

  • 相交链表 leetcode160
    • 暴力求解 map
    • 双指针1
    • 双指针2 巧妙 该部分源于leetcode官方题解

相交链表 leetcode160

由于链表深度不一致,所以不太好使用递归

  • 检测方法 :
    • 相交链表,即两个链表上一个节点的Next指向 同一个地址
    • 先检测同样Val值,再查看地址
      使用map?

同时这里要求我们返回的是相交节点的 Val

暴力求解 map

遍历


func getIntersectionNode(headA, headB *ListNode) *ListNode {//考虑到从第一个节点就相交的情况,设立虚拟头节点DummyHeadA := &ListNode{Next: headA}DummyHeadB := &ListNode{Next: headB}//使用map进行记录var resultA = map[*ListNode]int{}var resultB = map[*ListNode]int{}//遍历两个链表for DummyHeadA.Next != nil || DummyHeadB.Next != nil {if DummyHeadA.Next == nil {resultB[DummyHeadB.Next] = DummyHeadB.Next.ValDummyHeadB = DummyHeadB.Next} else if DummyHeadB.Next == nil {resultA[DummyHeadA.Next] = DummyHeadA.Next.ValDummyHeadA = DummyHeadA.Next} else {resultA[DummyHeadA.Next] = DummyHeadA.Next.ValDummyHeadA = DummyHeadA.NextresultB[DummyHeadB.Next] = DummyHeadB.Next.ValDummyHeadB = DummyHeadB.Next}//检查map中是否记录相交节点if _, ok := resultA[DummyHeadB]; ok {return DummyHeadB}if _, ok := resultB[DummyHeadA]; ok {return DummyHeadA}}return nil
}

双指针1

前面陷入思维误区,相交节点之后两个链表是完全一致的,所以我们可以忽略长链表比短链表长的一部分,
从长度一样的地方开始 两个链表同时遍历,相交节点一定位于同一级

// 双指针
func getIntersectionNode(headA, headB *ListNode) *ListNode {//遍历两链表得到链表长度La := 0Lb := 0curA := headAcurB := headBfor curA != nil {curA = curA.NextLa++}for curB != nil {curB = curB.NextLb++}//将较长的链表偏移到和较短的链表一样长的位置if La > Lb {for diff := La - Lb; diff > 0; diff-- {headA = headA.Next}}if La <= Lb {for diff := Lb - La; diff > 0; diff-- {headB = headB.Next}}//同级每个节点进行比较,相同则为相交节点for headA != nil {if headA == headB {return headA}headA = headA.NextheadB = headB.Next}return nil
}

双指针2 巧妙 该部分源于leetcode官方题解

思路和算法
使用双指针的方法,可以将空间复杂度降至 O(1)O(1)O(1)。

只有当链表 headA headB 都不为空时,两个链表才可能相交。因此首先判断链表 headA headB 是否为空,如果其中至少有一个链表为空,则两个链表一定不相交,返回 null

当链表 headA headB 都不为空时,创建两个指针 pApB,初始时分别指向两个链表的头节点headAheadB ,然后将两个指针依次遍历两个链表的每个节点。具体做法如下:

每步操作需要同时更新指针pA pB

如果指针 pA 不为空,则将指针 pA 移到下一个节点;如果指针 pB 不为空,则将指针 pB 移到下一个节点。

如果指针 pA为空,则将指针 pA 移到链表 headB 的头节点;如果指针 pB 为空,则将指针 pB 移到链表 headA 的头节点。

当指针pApB指向同一个节点或者都为空时,返回它们指向的节点或者 null

func getIntersectionNode(headA, headB *ListNode) *ListNode {if headA == nil || headB == nil {return nil}pa, pb := headA, headBfor pa != pb {if pa == nil {pa = headB} else {pa = pa.Next}if pb == nil {pb = headA} else {pb = pb.Next}}return pa
}

下面提供双指针方法的正确性证明。考虑两种情况,第一种情况是两个链表相交,第二种情况是两个链表不相交。

  • 情况一:两个链表相交

链表headAheadB 的长度分别是 mn。假设链表 headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 a+c=ma+c=ma+c=m,b+c=nb+c=nb+c=n

如果 a=b,则两个指针会同时到达两个链表相交的节点,此时返回相交的节点;

如果 a≠b ,则指针 pA 会遍历完链表 headA,指针 pB 会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 pA 移到链表 headB 的头节点,指针 pB 移到链表 headA 的头节点,然后两个指针继续移动,在指针 pA 移动了 a+c+b 次、指针 pB 移动了 b+c+a 次之后,两个指针会同时到达两个链表相交的节点,该节点也是两个指针第一次同时指向的节点,此时返回相交的节点。

  • 情况二:两个链表不相交

链表 headAheadB的长度分别是 mn 。考虑当 m=n m≠n 时,两个指针分别会如何移动:

如果 m=n ,则两个指针会同时到达两个链表的尾节点,然后同时变成空值 null,此时返回 null

如果 m≠n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针 pA 移动了m+n次、指针pB移动了n+m次之后,两个指针会同时变成空值 null,此时返回 null