「这是我参与11月更文挑战的第 11 天,活动详情查看:2021最后一次更文挑战」
刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能
两个链表的第一个公共结点
题目来源:LeetCode-160. 相交链表
题目描述
输入两个链表,找出它们的第一个公共节点
如下面的两个链表:
在节点 c1 开始相交
示例
示例 1
输入: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
输入: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
输入: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中的结点是否在散列表中,第一个在散列表中存在的结点,就是我们要找的第一个公共节点
代码
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时,长度差就消除了
文字描述很抽象,看图
//双指针实现
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
}