哈希表题目:相交链表

news2024/11/22 22:08:31

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
      • 进阶
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 证明
    • 代码
    • 复杂度分析

题目

标题和出处

标题:相交链表

出处:160. 相交链表

难度

2 级

题目描述

要求

给你两个单链表的头结点 headA \texttt{headA} headA headB \texttt{headB} headB,请你找出并返回两个单链表相交的起始结点。如果两个链表不存在相交结点,返回 null \texttt{null} null

图示两个链表在结点 c1 \texttt{c1} c1 开始相交:

示例 0

题目数据保证整个链式结构中不存在环。

注意,函数返回结果后,链表必须保持其原始结构

自定义评测:

评测系统的输入如下(你设计的程序不适用此输入):

  • intersectVal \texttt{intersectVal} intersectVal——相交的起始结点的值。如果不存在相交结点,这一值为 0 \texttt{0} 0
  • listA \texttt{listA} listA——第一个链表。
  • listB \texttt{listB} listB——第二个链表。
  • skipA \texttt{skipA} skipA——在 listA \texttt{listA} listA 中(从头结点开始)跳到交叉结点的结点数。
  • skipB \texttt{skipB} skipB——在 listB \texttt{listB} listB 中(从头结点开始)跳到交叉结点的结点数。

评测系统将根据这些输入创建链式数据结构,并将两个头结点 headA \texttt{headA} headA headB \texttt{headB} headB 传递给你的程序。如果程序能够正确返回相交结点,那么你的解决方案将被视作正确答案

示例

示例 1:

示例 1

输入: intersectVal   =   8,   listA   =   [4,1,8,4,5],   listB   =   [5,6,1,8,4,5],   skipA   =   2,   skipB   =   3 \texttt{intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3} intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出: Intersected   at   ‘8’ \texttt{Intersected at `8'} Intersected at ‘8’
解释:相交结点的值为 8 \texttt{8} 8(注意,如果两个链表相交则不能为 0 \texttt{0} 0)。
从各自的表头开始算起,链表 A \texttt{A} A [4,1,8,4,5] \texttt{[4,1,8,4,5]} [4,1,8,4,5],链表 B \texttt{B} B [5,6,1,8,4,5] \texttt{[5,6,1,8,4,5]} [5,6,1,8,4,5]
A \texttt{A} A 中,相交结点前有 2 \texttt{2} 2 个结点;在 B \texttt{B} B 中,相交结点前有 3 \texttt{3} 3 个结点。

示例 2:

示例 2

输入: intersectVal   =   2,   listA   =   [1,9,1,2,4],   listB   =   [3,2,4],   skipA   =   3,   skipB   =   1 \texttt{intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1} intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出: Intersected   at   ‘2’ \texttt{Intersected at `2'} Intersected at ‘2’
解释:相交结点的值为 2 \texttt{2} 2(注意,如果两个链表相交则不能为 0 \texttt{0} 0)。
从各自的表头开始算起,链表 A \texttt{A} A [1,9,1,2,4] \texttt{[1,9,1,2,4]} [1,9,1,2,4],链表 B \texttt{B} B [3,2,4] \texttt{[3,2,4]} [3,2,4]
A \texttt{A} A 中,相交结点前有 3 \texttt{3} 3 个结点;在 B \texttt{B} B 中,相交结点前有 1 \texttt{1} 1 个结点。

示例 3:

示例 3

输入: intersectVal   =   0,   listA   =   [2,6,4],   listB   =   [1,5],   skipA   =   3,   skipB   =   2 \texttt{intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2} intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出: No   intersection \texttt{No intersection} No intersection
解释:从各自的表头开始算起,链表 A \texttt{A} A [2,6,4] \texttt{[2,6,4]} [2,6,4],链表 B \texttt{B} B [1,5] \texttt{[1,5]} [1,5]
由于这两个链表不相交,所以 intersectVal \texttt{intersectVal} intersectVal 必须为 0 \texttt{0} 0,而 skipA \texttt{skipA} skipA skipB \texttt{skipB} skipB 可以是任意值。
这两个链表不相交,因此返回 null \texttt{null} null

数据范围

  • listA \texttt{listA} listA 中结点数目为 m \texttt{m} m
  • listB \texttt{listB} listB 中结点数目为 n \texttt{n} n
  • 1 ≤ m,   n ≤ 3 × 10 4 \texttt{1} \le \texttt{m, n} \le \texttt{3} \times \texttt{10}^\texttt{4} 1m, n3×104
  • 1 ≤ Node.val ≤ 10 5 \texttt{1} \le \texttt{Node.val} \le \texttt{10}^\texttt{5} 1Node.val105
  • 0 ≤ skipA ≤ m \texttt{0} \le \texttt{skipA} \le \texttt{m} 0skipAm
  • 0 ≤ skipB ≤ n \texttt{0} \le \texttt{skipB} \le \texttt{n} 0skipBn
  • 如果 listA \texttt{listA} listA listB \texttt{listB} listB 没有交点, intersectVal \texttt{intersectVal} intersectVal 0 \texttt{0} 0
  • 如果 listA \texttt{listA} listA listB \texttt{listB} listB 有交点, intersectVal = listA[skipA] = listB[skipB] \texttt{intersectVal} = \texttt{listA[skipA]} = \texttt{listB[skipB]} intersectVal=listA[skipA]=listB[skipB]

进阶

你能否设计一个时间复杂度 O(m   +   n) \texttt{O(m + n)} O(m + n)、空间复杂度 O(1) \texttt{O(1)} O(1) 的解法?

解法一

思路和算法

如果两个链表相交,则一定存在一个相交的起始结点,从链表相交的起始结点到链表末尾结点的全部结点是两个链表共用的结点。遍历两个链表各一次,则两个链表共用的结点会被重复访问,第一个被重复访问的结点即为链表相交的起始结点。

如果两个链表不相交,则不存在两个链表共用的结点。遍历两个链表各一次,任何结点都不会被重复访问。

因此可以遍历两个链表各一次,根据是否有结点被重复访问判断两个链表是否相交。为了判断是否有结点被重复访问,可以使用哈希集合存储访问过的结点。

首先遍历链表 listA \textit{listA} listA,将每个结点加入哈希集合,然后遍历链表 listB \textit{listB} listB,对于链表 listB \textit{listB} listB 中遍历到的每个结点判断是否在哈希集合中。在遍历链表 listB \textit{listB} listB 的过程中遇到的第一个在哈希集合中的结点即为链表相交的起始结点,返回该结点。如果遍历链表 listB \textit{listB} listB 结束之后仍没有遇到在哈希集合中的结点,则两个链表不相交,返回 null \text{null} null

代码

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Set<ListNode> visited = new HashSet<ListNode>();
        ListNode temp = headA;
        while (temp != null) {
            visited.add(temp);
            temp = temp.next;
        }
        temp = headB;
        while (temp != null) {
            if (visited.contains(temp)) {
                return temp;
            }
            temp = temp.next;
        }
        return null;
    }
}

复杂度分析

  • 时间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m n n n 分别是链表 listA \textit{listA} listA listB \textit{listB} listB 的结点数。需要遍历两个链表各一次。

  • 空间复杂度: O ( m ) O(m) O(m),其中 m m m 是链表 listA \textit{listA} listA 的长度。需要使用哈希集合存储链表 listA \textit{listA} listA 中的全部结点。

解法二

思路和算法

解法一使用哈希表,空间复杂度是 O ( m ) O(m) O(m)。使用双指针可以将空间复杂度降低到 O ( 1 ) O(1) O(1)

创建两个指针 pointer 1 \textit{pointer}_1 pointer1 pointer 2 \textit{pointer}_2 pointer2,指针 pointer 1 \textit{pointer}_1 pointer1 依次遍历链表 listA \textit{listA} listA 和链表 listB \textit{listB} listB 的每个结点,指针 pointer 2 \textit{pointer}_2 pointer2 依次遍历链表 listB \textit{listB} listB 和链表 listA \textit{listA} listA 的每个结点,初始时分别指向两个链表的头结点 headA \textit{headA} headA headB \textit{headB} headB

指针 pointer 1 \textit{pointer}_1 pointer1 的遍历过程是:从 headA \textit{headA} headA 开始依次遍历链表 listA \textit{listA} listA 的每个结点,当遍历完链表 listA \textit{listA} listA 之后, pointer 1 \textit{pointer}_1 pointer1 指向 null \text{null} null,下一步 pointer 1 \textit{pointer}_1 pointer1 指向 headB \textit{headB} headB,然后依次遍历链表 listB \textit{listB} listB 的每个结点,当遍历完链表 listB \textit{listB} listB 之后, pointer 1 \textit{pointer}_1 pointer1 指向 null \text{null} null

指针 pointer 2 \textit{pointer}_2 pointer2 的遍历过程和指针 pointer 1 \textit{pointer}_1 pointer1 的遍历过程相似,区别在于指针 pointer 2 \textit{pointer}_2 pointer2 先遍历链表 listB \textit{listB} listB 后遍历链表 listA \textit{listA} listA

两个指针的遍历同步进行,每次两个指针同时移动,直到两个指针指向同一个结点或者同时指向 null \text{null} null

  • 如果两个指针指向同一个结点,则该结点即为链表相交的起始结点,返回该结点。

  • 如果两个指针同时指向 null \text{null} null,则两个链表不相交,返回 null \text{null} null

证明

双指针解法的正确性证明需要考虑两种情况,第一种情况是两个链表相交,第二种情况是两个链表不相交。

如果两个链表相交,假设两个链表在相交之前的部分各有 x x x 个结点和 y y y 个结点,共用的部分有 z z z 个结点,则 x + z = m x + z = m x+z=m y + z = n y + z = n y+z=n,其中 m m m n n n 分别是链表 listA \textit{listA} listA listB \textit{listB} listB 的结点数。

  • 如果 x = y x = y x=y,则两个指针同时到达链表相交的起始结点。

  • 如果 x ≠ y x \ne y x=y,则指针 pointer 1 \textit{pointer}_1 pointer1 headA \textit{headA} headA 开始移动 m + y + 1 m + y + 1 m+y+1 次之后到达链表相交的起始结点(从 null \text{null} null 移动到 headB \textit{headB} headB 也是一次移动),指针 pointer 2 \textit{pointer}_2 pointer2 headB \textit{headB} headB 开始移动 n + x + 1 n + x + 1 n+x+1 次之后到达链表相交的起始结点(从 null \text{null} null 移动到 headA \textit{headA} headA 也是一次移动),由于 m + y + 1 = n + x + 1 = x + y + z + 1 m + y + 1 = n + x + 1 = x + y + z + 1 m+y+1=n+x+1=x+y+z+1,因此两个指针同时到达链表相交的起始结点。

如果两个链表不相交,则两个指针一定会在移动相同次数之后同时指向 null \text{null} null

  • 如果 m = n m = n m=n,则两个链表的结点数相同,两个指针遍历两个链表同时结束,因此同时指向 null \text{null} null

  • 如果 m ≠ n m \ne n m=n,则指针 pointer 1 \textit{pointer}_1 pointer1 headA \textit{headA} headA 开始移动 m + n + 1 m + n + 1 m+n+1 次之后指向 null \text{null} null(从 null \text{null} null 移动到 headB \textit{headB} headB 也是一次移动),指针 pointer 2 \textit{pointer}_2 pointer2 headA \textit{headA} headA 开始移动 n + m + 1 n + m + 1 n+m+1 次之后指向 null \text{null} null(从 null \text{null} null 移动到 headA \textit{headA} headA 也是一次移动),由于 m + n + 1 = n + m + 1 m + n + 1 = n + m + 1 m+n+1=n+m+1,因此两个指针同时指向 null \text{null} null

代码

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pointer1 = headA, pointer2 = headB;
        while (pointer1 != pointer2) {
            pointer1 = pointer1 == null ? headB : pointer1.next;
            pointer2 = pointer2 == null ? headA : pointer2.next;
        }
        return pointer1;
    }
}

复杂度分析

  • 时间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m n n n 分别是链表 listA \textit{listA} listA listB \textit{listB} listB 的结点数。最坏情况下,每个指针遍历两个链表各一次。

  • 空间复杂度: O ( 1 ) O(1) O(1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web网页设计期末课程大作业 基于HTML+CSS仿苹果商城电商项目的设计与实现

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

Thread类和线程状态

目录 Thread 的常见构造方法 Thread 的几个常见属性 经典面试题&#xff1a;start和run的区别 interrupt 线程中断 join 线程等待 currentThread 获得当前这个线程对应的 Thread 对象的引用。 sleep 置顶休眠的时间(阻塞一会) sleep(1000)&#xff0c;真的只是休眠1000…

使用 MindStudio进行基于 MindX SDK 辅助驾驶项目的开发

视频链接&#xff1a;https://www.bilibili.com/video/BV1K8411p7Cm/ 1 任务介绍 ASSISTED-DRIVING的中文含义为辅助驾驶&#xff0c;其先采用了yolov3模型将图片中的交通标志检测出来&#xff0c;然后利用resnet18模型获对检测出来的交通标志进行分类&#xff1b;在道路线分…

0-1规划下的数独问题

数独显然是一个0-1规划问题.虽然这个什么凸分析还是啥的分析有这个优化的方法,但是,你DFS也不是完全一点不可能的对吧.嗯,既然这样的话,我们就要去解决这样一个更细致的问题了.数独这个问题他的解的存在性,唯一性怎么样.当然,已经有结论了,一个9*9的数独至少要给出17个值才能约…

Qt扫盲-QGridLayout理论总结

QGridLayout理论总结1. 简介2. 操作布局元素3. 常用属性4. 间隔设置1. 简介 QGridLayout 占用来自其父布局或 parentWidget()获得的空间&#xff0c;将其划分为行列表格形式&#xff0c;在每个布局的表格里放置控件或者布局器就行。 一般来说&#xff0c;列和行的行为功能相同…

Sourcetree安装详细步骤

前言&#xff1a; Sourxetree 作为 免费的 Git 客户端工具&#xff0c;有许多优点。Sourcetree 简化了与Git存储库交互的方式&#xff0c;因此我们可以专注于编码。通过 Sourcetree 简单又快捷的管理我们 的存储库。 下载安装包 进入 官网 选择Windows系统的安装包 &#xff…

【大数据技术Hadoop+Spark】HDFS Shell常用命令及HDFS Java API详解及实战(超详细 附源码)

需要源码请点赞关注收藏后评论区留言私信~~~ 一、HDFS的Shell介绍 Shell在计算机科学中俗称“壳”&#xff0c;是提供给使用者使用界面的进行与系统交互的软件&#xff0c;通过接收用户输入的命令执行相应的操作&#xff0c;Shell分为图形界面Shell和命令行式Shell。 文件系统…

PTC Creo Illustrate生产技术设备

PTC Creo Illustrate生产技术设备 Creo Illustrator是一款适用于生产技术设备的软件。该软件将三种功能与当前CAD设计数据、技术规范以及所需和可能使用的各种部件相结合。这些工具对供应商和客户尤其有用。供应商可以使用本文档中提供的信息熟悉机器。查看内部零件&#xff0c…

YOLO系列目标检测算法——YOLOR

YOLO系列目标检测算法目录 - 文章链接 YOLO系列目标检测算法总结对比- 文章链接 YOLOv1- 文章链接 YOLOv2- 文章链接 YOLOv3- 文章链接 YOLOv4- 文章链接 Scaled-YOLOv4- 文章链接 YOLOv5- 文章链接 YOLOv6- 文章链接 YOLOv7- 文章链接 PP-YOLO- 文章链接 …

喜讯 | 美格智能子公司美格智联成功获选2022年首批国家级“高新技术企业”认定

近日&#xff0c;全国高新技术企业认定管理工作领导小组办公室发布了关于对深圳市认定机构2022年认定的第一批高新技术企业进行备案公示的通知&#xff0c;根据《高新技术企业认定管理办法》&#xff08;国科发火〔2016〕32号&#xff09;和《高新技术企业认定管理工作指引》&a…

[C/C++/初学者]500以内的亲密数对(VS2012)

在开始编写程序之前&#xff0c;我们需要了解一个东西。 何为亲密数对&#xff1f; 简单来说&#xff0c;就是数a的正因子数&#xff08;除本身外&#xff09;等于数b的正因子数&#xff08;除本身外&#xff09;。 符合这项条件的两个数&#xff0c;我们称他们为亲密数对。 …

[附源码]Python计算机毕业设计高校体育馆管理信息系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

146.个性化推荐系统案例介绍

YouTube的推荐系统算法由两个神经网络组成&#xff1a; 一个用于候选生成一个用于排序 以用户的浏览历史为输入&#xff0c;候选生成网络可以显著减小可推荐的视频数量&#xff0c;从庞大的库中选出一组最相关的视频 这样生成的候选视频与用户的相关性最高&#xff0c;然后会对…

【ROS话题通信】发布者和订阅者

前言 本文记录ROS话题通信的学习过程&#xff0c;便于后续复习。首先明确&#xff0c;ROS中的话题通信&#xff0c;在ROS通信中非常重要&#xff0c;实现了分布式发布接收消息&#xff0c;也是实现了不同编程语言间的解耦&#xff0c;下面记录下自己学习过程中的相关代码和配置…

Kotlin小菜——基础语言教程

概述 Kotlin是JetBrains推出的一种编程语言&#xff0c;JetBrains是最智能的Java IDE的官方设计器&#xff0c;名为Intellij IDEA。这是在JVM上运行的强静态类型语言。2017年&#xff0c;谷歌宣布Kotlin是Android开发的官方语言。Kotlin是一种开源编程语言&#xff0c;它将面向…

会话Cookie跟踪技术

会话 用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应。 从浏览器发出请求到服务端响应数据给前端之后&#xff0c;一次会话(在浏览器和服务器之间)就被建立了 …

产品设计:Material Design 学习笔记二

​5、图标 桌面图标 桌面图标尺寸是48dp X 48dp。 桌面图标建议模仿现实中的折纸效果&#xff0c;通过扁平色彩表现空间和光影。注意避免以下问题&#xff1a; ①不要给彩色元素加投影 ②层叠不要超过两层 ③折角不要放在左上角 ④带投影的元素要完整展现&#xff0c;不能…

Debug怎么用

文章目录前言一、打断点运行Debug二、页面重新运行功能三、回到代码看断点前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 因为最近有个项目注册不好使&#xff0c;想看看哪的问题&#xff0c;所以用debug看看程序走到了哪 提示&#xff1a;以下是本篇文…

docker部署mysql初始化数据库

一、前言 在使用docker部署mysql服务时&#xff0c;往往需要在创建容器时新建database&#xff0c;在需要在创建实例的过程中希望初始化sql脚本。 mysql官方镜像支持在容器初次启动时自动执行指定的sql脚本或shell脚本&#xff08;注意&#xff1a;只有初次启动时才能自动执行&…

【正点原子I.MX6U-MINI】通过tftp从Ubuntu中下载zImage 和设备树文件 | 从网络启动Linux系统

从网络启动linux系统的唯一目的就是为了调试&#xff01;不管是为了调试linux系统还是linux下的驱动。每次修改linux系统文件或者linux下的某个驱动以后都要将其烧写到EMMC中去测试&#xff0c;这样太麻烦了。我们可以设置linux从网络启动&#xff0c;也就是将 linux 镜像文件和…