LeetCode#160-Intersection of Two Linked Lists-相交链表
一、题目
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:

在节点 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) 内存。
二、题解
- 题解1:哈希表
遍历链表 A 并将每个节点的地址/引用存储在哈希表中。
然后检查链表 B 中的每一个节点是否在哈希表中。若在,则为相交结点。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
var s [] *ListNode
for headA != nil {
s = append(s, headA)
headA = headA.Next
}
for headB != nil {
for _,v := range(s) {
if v == headB {
return headB
}
}
headB = headB.Next
}
return nil
}
- 题解2:链表拼接法
当链表 A 走到尾部的 null 时,转到链表 B 的头节点继续走;
当链表 B 走到尾部的 null 时,转到链表 A 的头节点继续走;
若两链表相交,则 A 和 B 一定相遇。

如上图:初始化 pA = headA, pB = headB,开始遍历。
pA会先到达链表尾,当pA到达末尾时,重置pA为headB;同样的,当pB到达末尾时,重置pB为headA。当pA与pB相遇时,必然就是两个链表的交点。
为什么要这样处理?因为对 pA 而言,走过的路程为 a+c+b,对 pB 而言,为 b+c+a,显然 a+c+b = b+c+a,这就是该算法的核心原理。
即使两个链表没有相交点,仍然可以统一处理,因为这种情况意味着相交点就是 null,也就是上图中的公共部分c没有了,从而递推式变成了 pA: a+b,pB: b+a,同样是成立的。
时间复杂度:O(m+n),空间复杂度:O(1)。

/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(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
}
- 题解3:消除链表长度差
两个单链表,有公共结点,则必然尾部公用;
分别找出链表 1 和链表 2 的长度,长的链表减去短的链表得出一个 n 值;
长的链表先走 n 步,两个链表再同时移动,则两个链表相交点就是第一个公共结点。
时间复杂度:O(n),空间复杂度:O(1)。

/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}
lenA := getLen(headA)
lenB := getLen(headB)
//计算链表长度差 n,长的先移动 n 步
if (lenA > lenB) {// 链表A比链表B长,A先移动
for i := 0; i < lenA - lenB; i++ {
headA = headA.Next
}
} else {// 链表B比链表A长,B先移动
for i := 0; i < lenB - lenA; i++ {
headB = headB.Next
}
}
for headA != nil {
if headA == headB {
return headA
}
headA = headA.Next
headB = headB.Next
}
return nil;
}
//获取链表长度
func getLen(head *ListNode) int {
var len int
for head != nil {
len++
head = head.Next
}
return len
}
后记
这道题也是面试题中经常遇到的“两个链表的第一个公共节点”。虽然难度是“简单”,但对我来说一点也不简单。看评论和题解涨了很多姿势。
而且,一开始用的是 PHP,可怎么都通不过,不得不用 Go 重写了一遍,倒是很快通过了。
附上 PHP 版本:
- 题解1 PHP版本
class ListNode {
public $val = 0;
public $next = null;
function __construct($val) {
$this->val = $val;
}
}
/**
* @param ListNode $headA
* @param ListNode $headB
* @return ListNode
*/
function getIntersectionNode($headA, $headB) {
$hashMap = [];
while ($headA) {
$hashMap[] = $headA;
$headA = $headA->next;
}
while ($headB) {
if (in_array($headB, $hashMap)) {
return $headB;
}
$headB = $headB->next;
}
return null;
}
- 题解2 PHP版本
function getIntersectionNode($headA, $headB) {
if ($headA == null || $headB == null) {
return null;
}
$pA = $headA;
$pB = $headB;
while ($pA != $pB) {
$pA = $pA == null ? $headB : $pA->next;
$pB = $pB == null ? $headA : $pB->next;
}
return $pA;
}
- 题解3 PHP版本
function getIntersectionNode($headA, $headB) {
$lenA = getListLength($headA);
$lenB = getListLength($headB);
$diffLen = $lenA > $lenB ? $lenA - $lenB : $lenB - $lenA;
$headLong = $lenA > $lenB ? $headA : $headB;
$headShort = $lenA > $lenB ? $headB : $headA;
//先在长链表上走几步,再同时在两个链表上遍历
for ($i = 0; $i < $diffLen; $i++) {
$headLong = $headLong->next;
}
while ($headLong != $headShort) {
$headLong = $headLong->next;
$headShort = $headShort->next;
}
return $headLong;
}
/**
* 获取链表的长度
*/
function getListLength($head)
{
$length = 0;
$current = $head;
while ($current != null) {
$length++;
$current = $current->next;
}
return $length;
}
LeetCode#160-Intersection of Two Linked Lists-相交链表的更多相关文章
- 【LeetCode】Intersection of Two Linked Lists(相交链表)
这道题是LeetCode里的第160道题. 题目讲的: 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 示例 1: 输入:intersectVal = 8, ...
- 160 Intersection of Two Linked Lists 相交链表
编写一个程序,找到两个单链表相交的起始节点.例如,下面的两个链表:A: a1 → a2 ↘ ...
- Leetcode 160 Intersection of Two Linked Lists 单向链表
找出链表的交点, 如图所示的c1, 如果没有相交返回null. A: a1 → a2 ↘ ...
- [LeetCode] 160. Intersection of Two Linked Lists 解题思路
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- [LeetCode]160.Intersection of Two Linked Lists(2个链表的公共节点)
Intersection of Two Linked Lists Write a program to find the node at which the intersection of two s ...
- [LeetCode] 160. Intersection of Two Linked Lists 求两个链表的交集
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- LeetCode 160. Intersection of Two Linked Lists (两个链表的交点)
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- Leetcode 160. Intersection of two linked lists
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- Java for LeetCode 160 Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- ✡ leetcode 160. Intersection of Two Linked Lists 求两个链表的起始重复位置 --------- java
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
随机推荐
- 5L-链表导论心法
链表是比数组稍微复杂一点的数据结构,也是两个非常重要与基本的数据结构.如果说数组是纪律严明排列整齐的「正规军」那么链表就是灵活多变的「地下党」. 关注公众号 MageByte,有你想要的精彩内容. 链 ...
- Redis学习笔记1-java 使用Redis(jedis)
一.远程操作Redis 1. 在windows环境下安装RedisDesktopManager 2. 打开RedisDesktopManager 3. Add New Connection 4. 右击 ...
- GAN的五大有趣应用
引言 你能看出这张照片中面部的共同点吗? 这些人都不是真实存在的!这些面部图像都是由GAN技术生成的. "GAN"这个词是由Ian Goodfellow在2014年提出的,但相关概 ...
- SpringBoot 集成多数据源
一个项目中怎么划分数据库,可以通过具体业务需求. 项目中数据源怎么如何划分,通过注解的方式@Datasource(ref="") 在方法上指定,会连接指定的数据源,这种方式比较繁琐 ...
- Mybatis入门三
一.连接数据库的配置单独放在一个properties文件中 之前,我们是直接将数据库的连接配置信息写在了MyBatis的conf.xml文件中,如下: <?xml version="1 ...
- 深入理解Java AIO(一)—— Java AIO的简单使用
深入理解Java AIO(一)—— Java AIO的简单使用 深入理解AIO系列分为三个部分 第一部分也就是本节的Java AIO的简单使用 第二部分是AIO源码解析(只解析关键部分)(待更新) 第 ...
- python—os模块
os模块(操作目录) 1 import os 2 os.rename('旧','新') #修改文件名 3 os.remove('') #删除文件 4 print(os.listdir('.')) # ...
- Python查看3Dnii文件
from nibabel.viewers import OrthoSlicer3D from nibabel import nifti1 import nibabel as nib from matp ...
- Javascript/Jquery实现日期前一天后一天
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...
- Java并发基础08. 造成HashMap非线程安全的原因
在前面我的一篇总结(6. 线程范围内共享数据)文章中提到,为了数据能在线程范围内使用,我用了 HashMap 来存储不同线程中的数据,key 为当前线程,value 为当前线程中的数据.我取的时候根据 ...