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

1,275 阅读4分钟

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

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

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

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

题目描述

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

如下面的两个链表:

1.png

在节点 c1 开始相交

示例

示例 1

2.png

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

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

输入: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时,长度差就消除了

文字描述很抽象,看图

image.png

//双指针实现
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
}