高频算法面试题(二十一)- 两个链表的第一个公共结点

「这是我参与11月更文挑战的第 11 天,活动详情查看:2021最后一次更文挑战

刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能

两个链表的第一个公共结点

题目来源LeetCode-160. 相交链表

题目描述

输入两个链表,找出它们的第一个公共节点

如下面的两个链表:

1.png

在节点 c1 开始相交

示例

示例 1

2.png

1
2
3
css复制代码输入: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.png

1
2
3
css复制代码输入: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.png

1
2
3
4
css复制代码输入: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) 内存
  • 本题与主站 160 题相同:leetcode-cn.com/problems/in…

解题

解法一:散列表

思路

这种找公共节点,跟找环的入口点差不多,所以解题的思路也差不多,过程是

  • 先遍历链表l1,并将结点记录到散列表中
  • 再遍历l2,遍历的过程中,判断l2中的结点是否在散列表中,第一个在散列表中存在的结点,就是我们要找的第一个公共节点

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
go复制代码type ListNode struct {
Val int
Next *ListNode
}
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}
mapNode := make(map[*ListNode]bool)
for ;headA != nil; headA = headA.Next {
mapNode[headA] = true
}
for ;headB != nil;headB = headB.Next {
if _, ok := mapNode[headB]; ok {
return headB
}
}

return nil
}

解法二:双指针

思路

这种感觉很难想到,当花一定的时间还是想不到怎么做的时候,可以去看别人的做法。毕竟有很多解法很难想到,主要是理解思路,知道这种问题,有这种解法,不必过于死磕

首先我们知道,如果两个链表相交,那么它们第一个相交点之后的长度是相等的。只要我们能消除相交之前部分的长度差,当两个指针相等的时候,就是它们的相交点,具体过程如下:

  • 指针pA指向 A 链表,指针 pB 指向 B 链表,依次往后遍历
  • 如果 pA 到了末尾,则让pA指向B链表的头结点( pA = headB )继续遍历
  • 如果 pB 到了末尾,则则让pB指向A链表的头结点( pB = headA )继续遍历
  • 比较长的链表指针指向较短链表head时,长度差就消除了

文字描述很抽象,看图

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ini复制代码//双指针实现
func getIntersectionNode1(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}

pA, pB := headA, headB
for pA != pB {
if pA == nil {
pA = headB
} else {
pA = pA.Next
}
if pB == nil {
pB = headA
} else {
pB = pB.Next
}
}

return pA
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%