【经典算法】LeetCode 160. 相交链表(Java/C/Python3/Go实现含注释说明,Easy)

目录

  • 题目描述
  • 思路及实现
    • 方式一:哈希表
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
        • Golang版本
      • 复杂度分析
    • 方式二:双指针
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
        • Golang版本
      • 复杂度分析
  • 总结
  • 相似题目

  • 标签(题目类型):链表

题目描述

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

图示两个链表在节点 c1 开始相交:
在这里插入图片描述

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

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

自定义评测:

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

intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 1:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2:
在这里插入图片描述

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,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 。

提示:
listA 中节点数目为 m
listB 中节点数目为 n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

原题:LeetCode 160. 相交链表

思路及实现

方式一:哈希表

思路

使用哈希表来存储链表中的节点。遍历第一个链表的所有节点,将它们存储在哈希表中。然后遍历第二个链表,如果某个节点已经在哈希表中,那么这个节点就是两个链表的相交点。

代码实现

Java版本
public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

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

说明:使用HashSet来存储链表A的节点,然后遍历链表B,检查每个节点是否在HashSet中出现过。如果出现过,则返回该节点;否则返回null

C语言版本
#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode {
    int val;
    struct ListNode *next;
} ListNode;

ListNode* getIntersectionNode(ListNode *headA, ListNode *headB) {
    // C语言中没有内置的哈希表,需要使用其他数据结构(如数组、链表)来实现类似功能
    // 这里为了简化,不给出完整实现
    // ...
    return NULL; // 示例返回,实际需要根据具体实现返回结果
}

说明:C语言中没有内置的哈希表,需要自行实现或使用其他数据结构(如链表)来模拟哈希表的功能。这里为了简化,不给出完整实现。

Python3版本
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        setA = set()
        while headA:
            setA.add(headA)
            headA = headA.next
        while headB:
            if headB in setA:
                return headB
            headB = headB.next
        return None

说明:使用Python的set数据结构来存储链表A的节点,然后遍历链表B,检查每个节点是否在setA中出现过。

Golang版本
package main

import (
    "fmt"
)

type ListNode struct {
    Val  int
    Next *ListNode
}

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    setA := make(map[*ListNode]bool)
    for node := headA; node != nil; node = node.Next {
        setA[node] = true
    }
    for node := headB; node != nil; node = node.Next {
        if _, ok := setA[node]; ok {
            return node
        }
    }
    return nil
}

func main() {
    // 示例代码,未完整实现链表创建和测试
    // ...
}

说明:使用Golang的map数据结构来存储链表A的节点,然后遍历链表B,检查每个节点是否在setA中出现过。

复杂度分析

  • 时间复杂度:O(m + n),其中m和n分别为两个链表的长度。因为需要遍历两个链表各一次。
  • 空间复杂度是O(m),其中m是链表A的长度。

说明
在方式一中,我们使用了一个哈希表(通常是哈希集合或哈希映射)来存储链表A中的节点。假设链表A的长度为m,链表B的长度为n,那么在最坏的情况下,我们需要将链表A中的所有m个节点都存储到哈希表中。
因此,哈希表将占用O(m)的空间(假设哈希表的空间复杂度是线性的,这取决于具体的哈希表实现)。除了哈希表之外,我们没有使用额外的数据结构,因此总的空间复杂度是O(m)。
*注意,这里我们没有考虑哈希表内部可能存在的空间开销(如哈希函数的计算、哈希冲突的解决等),因为这些通常与节点数量m成常数比例或对数比例,并且对于分析算法的空间复杂度来说,通常可以忽略不计。

方式二:双指针

思路

我们可以使用双指针法来解决这个问题。首先,让两个指针pApB分别指向两个链表的头节点headAheadB。然后,让它们同时遍历两个链表。当指针pA到达链表A的尾部时,让它指向链表B的头节点headB;当指针pB到达链表B的尾部时,让它指向链表A的头节点headA。这样,如果两个链表相交,两个指针最终会在交点处相遇;如果两个链表不相交,两个指针最终都会指向null

代码实现

Java版本
public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        ListNode pA = headA, pB = headB;
        while (pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

说明:使用两个指针pApB同时遍历两个链表。如果pA到达链表A的尾部,则将其指向headB;如果pB到达链表B的尾部,则将其指向headA。当两个指针相遇或都为null时,遍历结束。

C语言版本
#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode {
    int val;
    struct ListNode *next;
} ListNode;

ListNode* getIntersectionNode(ListNode *headA, ListNode *headB) {
    if (headA == NULL || headB == NULL) {
        return NULL;
    }
    ListNode *pA = headA, *pB = headB;
    while (pA != pB) {
        pA = pA == NULL ? headB : pA->next;
        pB = pB == NULL ? headA : pB->next;
    }
    return pA;
}

说明:C语言版本的实现与Java版本类似,只是语法和类型定义有所不同。

Python3版本
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        if not headA or not headB:
            return None
        pA, pB = headA, headB
        while pA != pB:
            pA = pA.next if pA else headB
            pB = pB.next if pB else headA
        return pA

说明:Python版本的实现使用了类和方法,但核心逻辑与Java和C语言版本相同。

Golang版本
package main

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
}

func main() {
    // 示例代码,未完整实现链表创建和测试
    // ...
}

说明:Golang版本的实现使用了结构体和指针,核心逻辑与其他版本相同。

复杂度分析

  • 时间复杂度:O(m + n),其中m和n分别为两个链表的长度。因为最多遍历两个链表各两次(当两个链表不相交时)。
  • 空间复杂度:O(1),因为只使用了常数个额外变量。

总结

方式优点缺点时间复杂度空间复杂度
方式一(哈希表)不依赖链表结构,灵活性强需要额外的哈希表空间O(m + n)O(m)
方式二(双指针)代码简洁,空间复杂度低需要处理两个链表可能不相交的情况O(m + n)O(1)

相似题目

相似题目难度链接
leetcode 160 相交链表简单力扣-160
leetcode 349 两个数组的交集简单力扣-349
leetcode 350 两个数组的交集 II简单力扣-350
面试题 16.07 交集中等面试题 16.07

请注意,上述链接均为中文版的力扣链接,如果需要英文版的链接,请将leetcode-cn.com替换为leetcode.com

此外,虽然这些题目在表面上看似不同(例如,有的是关于链表,有的是关于数组),但它们的核心思路都是寻找两个集合(链表或数组)的交集。在处理这些题目时,可以考虑使用哈希表、双指针等不同的方法来优化算法的性能。

欢迎一键三连(关注+点赞+收藏),技术的路上一起加油!!!代码改变世界

  • 关于我:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名),回复暗号,更能获取学习秘籍和书籍等

  • —⬇️欢迎关注下面的公众号:进朱者赤,认识不一样的技术人。⬇️—

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言——操作符保姆级教学(含整形提升及算数转换)

操作符 一.操作符的分类二.原码、反码、补码三.移位操作符1.左移操作符&#xff1a;<<2.右移操作符&#xff1a;>> 四.位操作符1.按位与—— &2.按位或—— |3.按位异或—— ^4.按位取反—— ~ 五.逗号表达式六.条件操作符七.操作符的属性&#xff1a;优先级、…

如何配置和使用Apollo的component里的plugin

关于如何使用Apollo的Component里的plugin&#xff0c;在Apollo的文档里只有如果和开发的说明却没有找到一个清楚完整说明怎么把plugin跑起来的说明&#xff0c;例如我想把lidar_detection_filter按我们的需求对目标过滤算法作修改然后编译完后&#xff0c;执行 cyber_launch …

【数据结构】链表专题3

前言 本篇博客我们继续来讨论链表专题&#xff0c;今天的链表算法题是经典中的经典 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;数据结构 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 目录 1.判断链表是否…

Dom获取属性操作

目录 1. 基本认知 1.1 目的和内容 1.2 什么是DOM 1.3 DOM对象 1.4 DOM树 2. 获取DOM元素对象 2.1 选择匹配到的第一个元素 2.2 选择匹配到的多个元素 2.3 其他获取DOM元素方法 3. 操作元素内容 3.1 元素对象.innerText 属性 3.2 元素对象.innerHTML 属性 4. 操作元…

springcloud微服务搭建多数据源(mysql,oracle,postgres,等等)管理模块,支持通过注解方式切换不同类型的数据库

1.背景 同一套微服务管理系统&#xff0c;业务完全一样&#xff0c;但不同的客户可能要求使用自己熟悉的数据库&#xff0c;比如&#xff0c;mysql&#xff0c;oracle&#xff0c;postgres&#xff0c;还有一些国产数据库。如果能够将数据库模块独立出来&#xff0c;兼容各家的…

IDEA启动项目报错:Error running ‘‘: Command line is too long.

1、在workspace.xml 2、 在标签 <component name"PropertiesComponent"> 添加 <property name"dynamic.classpath" value"true" />

MySQL 运维篇

回顾基本语句&#xff1a; 数据定义语言(DDL) 这类语言用于定义和修改数据库的结构&#xff0c;包括创建、删除和修改数据库、 表、视图和索引等对象。 主要的语句关键字包括 CREATE 、 DROP 、 ALTER 、 RENAME 、 TRUNCATE 等。 create database 数据库 &#xff1b; cr…

【MySQL | 第十一篇】一条SQL语句在MySQL的执行过程

文章目录 11.一条SQL语句在MySQL的执行过程11.1MySQL整体架构11.2SQL语句在Server层执行流程11.3拓展&#xff1a;InnoDB存储引擎的更新操作11.3.1问题&#xff1a;为什么写了redolog日志还要写binlog日志&#xff1f;11.3.2问题&#xff1a;为什么要两阶段提交&#xff1f;11.…

《QT实用小工具·四十七》可交互的创意动态按钮

1、概述 源码放在文章末尾 该项目实现了可交互的创意动态按钮&#xff0c;包含如下功能&#xff1a; 所有颜色自定义 鼠标悬浮渐变 两种点击效果&#xff1a;鼠标点击渐变 / 水波纹动画&#xff08;可多层波纹叠加&#xff09; 额外鼠标移入/移出/按下/弹起的实时/延迟共8种事…

springboot 自动配置源码解读

什么是自动装配 当我们程序依赖第三方功能组件时&#xff0c;不需要手动将这些组件类加载到IOC容器中。例如 当程序需要用到redis时&#xff0c;在pom.xml文件中引入依赖&#xff0c;然后使用依赖注入的方式直接从IOC容器中拿到相应RedisTemplate实例。 SpringBootApplication …

【已解决】json文件太大无法打开怎么办+ijson报错

下载了一个json文档&#xff0c;尝试了全部的编辑器都打不开。。。 搜了搜或许可以使用ijson GitHub - ICRAR/ijson: Iterative JSON parser with Pythonic interfaces "Ijson is an iterative JSON parser with standard Python iterator interfaces." 示例代码&…

【C++ —— 多态】

C —— 多态 多态的概念多态的定义和实现多态的构成条件虚函数虚函数的重写虚函数重写的两个例外协变&#xff1a;析构函数的重写 C11 override和final重载、覆盖(重写)、隐藏(重定义)的对比 抽象类概念接口继承和实现继承 多态的继承虚函数表多态的原理动态绑定和静态绑定 单继…

VTK 的可视化方法:Glyph

VTK 的可视化方法&#xff1a;Glyph VTK 的可视化方法&#xff1a;Glyph标量、向量、张量将多边形数据的采集点法向量标记成锥形符号参考 VTK 的可视化方法&#xff1a;Glyph 模型的法向量数据是向量数据&#xff0c;因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通…

25 JavaScript学习:var let const

JavaScript全局变量 JavaScript中全局变量存在多种情况和定义方式&#xff0c;下面详细解释并提供相应的举例&#xff1a; 使用var关键字声明的全局变量&#xff1a; var globalVar "我是全局变量";未使用var关键字声明的变量会成为全局变量&#xff08;不推荐使用&…

【前端】-【防止接口重复请求】

文章目录 需求实现方案方案一方案二方案三 需求 对整个的项目都做一下接口防止重复请求的处理 实现方案 方案一 思路&#xff1a;通过使用axios拦截器&#xff0c;在请求拦截器中开启全屏Loading&#xff0c;然后在响应拦截器中将Loading关闭。 代码&#xff1a; 问题&…

(刷题记录2)随机链表的复制

[刷题记录2]随机链表的复制 题目信息&#xff1a;题目思路(环境来自力扣OJ的C语言)&#xff1a;复杂度&#xff1a;代码和解释&#xff1a;1.遍历一遍原链表的同时&#xff0c;在每个原节点后面插入一个相同的新节点&#xff0c;共插入 n 个新节点。2.利用两者联系&#xff0c;…

神奇的Vue3 - 组件探索

神奇的Vue3 第一章 神奇的Vue3—基础篇 第二章 神奇的Vue3—Pinia 文章目录 神奇的Vue3了解组件一、注册组件1. 全局注册​2. 局部注册3. 组件命名 二、属性详解1. Props&#xff08;1&#xff09;基础使用方法&#xff08;2&#xff09;数据流向&#xff1a;单项绑定原则&…

ThreeJS:Mesh网格与三维变换

Mesh网格 ThreeJS中&#xff0c;Mesh表示基于以三角形为多边形网格(polygon mesh)的物体的类&#xff0c;同时也作为其它类的基类。 通过Mesh网格&#xff0c;我们可以组合Geometry几何体与Material材质属性&#xff0c;在3D世界中&#xff0c;定义一个物体。例如&#xff1a;之…

vue2(4)之scoped解决样式冲突/组件通信/非父子通信/ref和$refs/异步更新/.sync/事件总线/provide和inject

vue2 一、学习目标1.组件的三大组成部分&#xff08;结构/样式/逻辑&#xff09;2.组件通信3.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xff09;4.进阶语法 二、scoped解决样式冲突**1.默认情况**&#xff1a;2.代码演示3.scoped原理4.总结 三、data必须是一个函数…

Copilot Venture Studio創始合伙人楊林苑確認出席“邊緣智能2024 - AI開發者峰會”

隨著AI技術的迅猛發展&#xff0c;全球正逐步進入邊緣計算智能化與分布式AI深度融合的新時代&#xff0c;共同書寫著分布式智能創新應用的壯麗篇章。邊緣智能&#xff0c;作為融合邊緣計算和智能技術的新興領域&#xff0c;正逐漸成為推動AI發展的關鍵力量。借助分布式和去中心…