【C语言 - 哈希表 - 力扣 - 相交链表】

news2024/11/24 14:41:18

相交链表题目描述

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

在这里插入图片描述
题目数据 保证 整个链式结构中不存在环。

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

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

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

题解

方法一:哈希集合

判断两个链表是否相交,可以使用哈希集合存储链表节点。

首先遍历链表 headA,并将链表 headA 中的每个节点加入哈希集合中。然后遍历链表 headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:

如果当前节点不在哈希集合中,则继续遍历下一个节点;

如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,因此在链表 headB 中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。

如果链表 headB 中的所有节点都不在哈希集合中,则两个链表不相交,返回 null。

// 定义哈希表结构体
struct HashTable {
    struct ListNode *key; // 哈希表的键,指向链表节点
    UT_hash_handle hh;    // 哈希表的特殊域,用于管理哈希表
};

// 函数:获取两个链表的交点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    // 初始化哈希表
    struct HashTable *hashTable = NULL;
    // 遍历链表 headA,将节点添加到哈希表中
    struct ListNode *temp = headA;
    while (temp != NULL) {
        // 临时指针
        struct HashTable *tmp;
        // 在哈希表中查找当前节点是否存在
        HASH_FIND(hh, hashTable, &temp, sizeof(struct HashTable *), tmp);
        // 如果节点不存在于哈希表中,则将其加入哈希表
        if (tmp == NULL) {
            tmp = malloc(sizeof(struct HashTable));
            tmp->key = temp;
            HASH_ADD(hh, hashTable, key, sizeof(struct HashTable *), tmp);
        }
        // 继续遍历下一个节点
        temp = temp->next;
    }
    // 遍历链表 headB,查找是否存在于哈希表中的节点
    temp = headB;
    while (temp != NULL) {
        // 临时指针
        struct HashTable *tmp;
        // 在哈希表中查找当前节点是否存在
        HASH_FIND(hh, hashTable, &temp, sizeof(struct HashTable *), tmp);
        // 如果找到了交点,则直接返回该节点
        if (tmp != NULL) {
            return temp;
        }
        // 继续遍历下一个节点
        temp = temp->next;
    }
    // 如果遍历完链表 headB 都没有找到交点,则返回 NULL
    return NULL;
    // 这种方法利用了哈希表的快速查找特性,将时间复杂度从线性降低到了接近常数级别。
    // 总体而言,这段代码展示了哈希表在解决链表相关问题中的应用,特别是在寻找交点等场景下能够提供高效的解决方案。
}

哈希表

哈希表(Hash Table),也称为散列表,是一种常用的数据结构,用于实现关联数组。它通过将键(key)映射到数组(Array)的特定位置来实现快速的数据检索。哈希表的主要思想是利用哈希函数将键转换为数组索引,然后将值存储在该索引位置的数组中。

哈希表的基本结构包括以下几个重要组成部分:

  1. 哈希函数(Hash Function):哈希函数是哈希表的核心,它负责将键映射到数组的特定位置。良好的哈希函数应该具有以下特性:

    • 易于计算:哈希函数应该能够快速计算出哈希值。
    • 均匀分布:哈希函数应该能够将键均匀地分布在数组中,以减少冲突的发生。
    • 最小冲突:哈希函数应该能够尽量减少键的冲突,即不同的键映射到相同的数组索引的情况。
  2. 数组(Array):哈希表使用数组来存储键值对。每个数组位置称为“桶”(Bucket),一个桶可以存储一个或多个键值对。当发生哈希冲突时,通常使用一种解决冲突的方法来处理,比如链地址法或开放地址法。

  3. 解决冲突的方法:由于不同的键可能会映射到相同的数组索引位置,所以哈希表需要一种解决冲突的方法。常见的方法包括:

    • 链地址法(Chaining):将具有相同哈希值的键值对存储在同一个桶中的链表或其他数据结构中。
    • 开放地址法(Open Addressing):当发生冲突时,通过探查数组中的其他位置来寻找空闲的位置,并将键值对插入到空闲位置中。

哈希表的时间复杂度取决于哈希函数的性能和冲突解决方法的效率。在理想情况下,哈希表可以实现常数时间复杂度的查找、插入和删除操作(O(1)),但在最坏情况下,可能会退化到线性时间复杂度(O(n))。

哈希表被广泛应用于各种编程语言的标准库中,用于实现诸如字典(Dictionary)、集合(Set)等数据结构,以及在数据库中用于加快数据检索速度等场景。

方法二:双指针

思路和算法

使用双指针的方法,可以将空间复杂度降至 O(1)。

只有当链表 headA 和 headB 都不为空时,两个链表才可能相交。因此首先判断链表 headA 和 headB 是否为空,如果其中至少有一个链表为空,则两个链表一定不相交,返回 null。

当链表 headA 和 headB 都不为空时,创建两个指针 pA 和 pB,初始时分别指向两个链表的头节点 headA 和 headB,然后将两个指针依次遍历两个链表的每个节点。具体做法如下:

每步操作需要同时更新指针 pA 和 pB。

如果指针 pA 不为空,则将指针 pA 移到下一个节点;如果指针 pB 不为空,则将指针 pB 移到下一个节点。

如果指针 pA 为空,则将指针 pA 移到链表 headB 的头节点;如果指针 pB 为空,则将指针 pB 移到链表 headA 的头节点。

当指针 pA 和 pB 指向同一个节点或者都为空时,返回它们指向的节点或者 null。

证明

下面提供双指针方法的正确性证明。考虑两种情况,第一种情况是两个链表相交,第二种情况是两个链表不相交。

情况一:两个链表相交

链表headA 和 headB 的长度分别是 m 和 n。假设链表 headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 a+c=m,b+c=n。

如果 a=b,则两个指针会同时到达两个链表相交的节点,此时返回相交的节点;

如果 a≠b,则指针 pA 会遍历完链表 headA,指针 pB 会遍历完链表 headB,两个指针不会同时到达链表的尾节点,然后指针 pA 移到链表 headB 的头节点,指针 pB 移到链表 headA 的头节点,然后两个指针继续移动,在指针 pA 移动了 a+c+b 次、指针 pB 移动了 b+c+a 次之后,两个指针会同时到达两个链表相交的节点,该节点也是两个指针第一次同时指向的节点,此时返回相交的节点。

情况二:两个链表不相交

链表 headA 和 headB 的长度分别是 m 和 n。考虑当 m=n 和 m≠n 时,两个指针分别会如何移动:

如果 m=n,则两个指针会同时到达两个链表的尾节点,然后同时变成空值 null,此时返回 null;

如果 m≠n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针 pA 移动了 m+n 次、指针 pB 移动了 n+m 次之后,两个指针会同时变成空值 null,此时返回 null。

// 函数:获取两个链表的交点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    // 如果其中一个链表为空,则直接返回 NULL,因为没有交点
    if (headA == NULL || headB == NULL) {
        return NULL;
    }
    // 初始化两个指针 pA 和 pB 分别指向链表 headA 和 headB 的头节点
    struct ListNode *pA = headA, *pB = headB;
    // 当 pA 不等于 pB 时循环,即两个指针没有相遇
    while (pA != pB) {
        // 如果 pA 到达了链表 headA 的末尾,则将 pA 指向链表 headB 的头节点
        pA = pA == NULL ? headB : pA->next;
        // 如果 pB 到达了链表 headB 的末尾,则将 pB 指向链表 headA 的头节点
        pB = pB == NULL ? headA : pB->next;
    }
    // 返回 pA(或 pB),即两个链表的交点,如果没有交点则返回 NULL
    return pA;
}

帮助理解

将2个链表在末尾加上对方,碰到第一个相同的点,要么是交点,要么是末尾

situation 1

A: 1 -> 2 -> 3 -> C -> 4 -> 5 -> null

B: 6 -> 7 -> C -> 4 -> 5 -> null

A + B: 1 -> 2 -> 3 -> C -> 4 -> 5 -> 6 -> 7 -> C -> 4 -> 5 -> null

B + A: 6 -> 7 -> C -> 4 -> 5 -> 1 -> 2 -> 3 -> C -> 4 -> 5 -> null

situation 2

A: 1 -> 2 -> 3 -> 4 -> 5 -> null

B: 6 -> 7 -> 8 -> 3 -> null

A + B: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 3 -> null

B + A: 6 -> 7 -> 8 -> 3 -> 1 -> 2 -> 3 -> 4 -> 5 -> null

作者:力扣官方题解
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists/solutions/811625/xiang-jiao-lian-biao-by-leetcode-solutio-a8jn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

92.网游逆向分析与插件开发-游戏窗口化助手-显示游戏数据到小助手UI

内容参考于:易道云信息技术研究院VIP课 上一个内容:游戏窗口化助手的UI设计-CSDN博客 码云地址(游戏窗口化助手 分支):https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号:e8116af3a7b0186adba…

gdb或者asan生成的coredump文件显示的错误行号和实际的不匹配问题

原因: 是^M这个东西引起的,vim看的时候会少一行多一个^M,但是windos看的时候没有^M,但是会多一行,所以生成的core文件显示的行号和vim看到的不匹配,几个^M就多几行,但是和windos下看文本的行号一…

六轴机器人奇异点

1 奇异点说明 有着6个自由度的KUKA机器人具有3个不同的奇点位置。即便在给定状态和步骤顺序的情况下,也无法通过逆向变换(将笛卡尔坐标转换成极坐标值)得出唯一数值时,即可认为是一个奇点位置。这种情况下,或者当最小的笛卡尔变化也能导致非常大的轴角度变化时,即为奇点位置…

vue3+echarts:Vue中使用echarts从后端获取数据并赋值显示

//由于前后端交互,所以使用axios发送请求 const Count ref(null); //设备种类数值 const Name ref(null); //设备种类名称 //设备种类 饼图 const pieChart () > {const getpieChart echarts.init(document.getElementById("deviceKind"));// 创建图标getpieC…

如何使用Docker部署DashDot服务器仪表盘并结合cpolar实现公网访问

📑前言 本文主要是使用Docker部署DashDot服务器仪表盘并结合cpolar实现公网访问的文章,如果有什么需要改进的地方还请大佬指出⛺️** 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风…

Leetcode—60. 排列序列【困难】

2024每日刷题&#xff08;113&#xff09; Leetcode—60. 排列序列 算法思想 实现代码 class Solution { public:string getPermutation(int n, int k) {vector<int> nums(n);// f[i] i!vector<int> f(n 1, 1); string ans;iota(nums.begin(), nums.end(), 1…

一文带你了解量子计算的力量

作为技术人员&#xff0c;我们知道有有四项技术一直在进行着巨大的进步——区块链、人工智能、物联网和量子计算机。本文中&#xff0c;我们将讨论量子计算机&#xff0c;包括它的含义&#xff0c;如何工作&#xff0c;它们将如何使用等。 什么是量子计算机&#xff1f; 量子计…

蓝桥杯Web应用开发-浮动与定位

浮动与定位 浮动布局比较灵活&#xff0c;不易控制&#xff0c;而定位可以控制元素的过分灵活性&#xff0c;给元素一个具体的空间和精确的位置。 浮动 我们使用 float 属性指定元素沿其容器的左侧或右侧放置&#xff0c;浮动布局常见取值如下&#xff1a; • left&#xff0…

2023:AI疯狂进化年

嘿&#xff0c;大家好&#xff01;让我们一起来回顾一下这疯狂的 2023 年吧&#xff01;记得那个二月初吗&#xff1f;ChatGPT 上线了&#xff0c;然后呢&#xff1f;短短两个月&#xff0c;用户数量就像火箭一样突破了 1 亿&#xff01;这速度&#xff0c;简直比超级赛亚人还快…

谷歌seo搜索引擎优化教程有吗?

教程&#xff0c;教学&#xff0c;指南&#xff0c;这些东西哪里都有&#xff0c;尤其是关于seo相关方面的&#xff0c;这些可以说到处都是&#xff0c;能把谷歌seo这个关键词做上去的&#xff0c;可以说就是实力的证明了&#xff0c;在这里我们说一个无论是老手还是新手都应该…

(五)elasticsearch 源码之查询流程分析

https://www.cnblogs.com/darcy-yuan/p/17039526.html 1.概述 上文我们讨论了es&#xff08;elasticsearch&#xff0c;下同&#xff09;索引流程&#xff0c;本文讨论es查询流程&#xff0c;以下是基本流程图 2.查询流程 为了方便调试代码&#xff0c;笔者在电脑上启动了了…

通义千问上线春节新应用,AI帮你免费拍全家福

2月5日&#xff0c;春节将至年味渐浓&#xff0c;阿里云通义千问APP上线多项免费新应用&#xff0c;涵盖全家福、拜新年、万物成龙等图像生成的新玩法&#xff0c;共提供超300套照片模板&#xff0c;用户上传照片即可生成全家福、团圆照、拜年照、千里江山主题照&#xff1b;此…

引用大佬讲座谈 2100万日活FPS手游怎样做优化?百元机稳定30帧、适配98%机型

大型手机游戏对不同手机机型的适配&#xff0c;始终是贯穿手游发展过程的挑战。尤其在前几年&#xff0c;高配置的智能机还没有普及开的时候&#xff0c;产品优化是研发环节的重中之重。而作为对于网络状况、数据传输和服务器架构等有着极高要求的游戏品类&#xff0c;FPS更是优…

Javaweb之SpringBootWeb案例之 登录功能的详细解析

1. 登录功能 1.1 需求 在登录界面中&#xff0c;我们可以输入用户的用户名以及密码&#xff0c;然后点击 "登录" 按钮就要请求服务器&#xff0c;服务端判断用户输入的用户名或者密码是否正确。如果正确&#xff0c;则返回成功结果&#xff0c;前端跳转至系统首页面…

重学Java 13.面向对象.1

在熟悉的事物中循环 ——24.2.7 一、static关键字 1.static关键字的介绍以及基本使用 1.概述&#xff1a;static是一个静态关键字 2.使用&#xff1a; a.修饰一个成员变量&#xff1a; static 数据类型 变量名 b.修饰一个方法&#xff1a; 修饰符 static 返回值类型 方法名&am…

2024年,前景最被看好的十大行业

哪些行业在未来更具增长潜力&#xff1f;资本市场给出的答案&#xff0c;可以从上市公司的“行业市盈率”看出&#xff0c;但传统的行业市盈率方法需要改造。 “市盈率”简称PE&#xff0c;是股票价格与每股收益之间的比值。比如&#xff0c;某股票价格10元&#xff0c;某年的…

Vue中对虚拟DOM的理解

作为现代前端开发中的主流框架之一&#xff0c;Vue.js是一个非常流行的JavaScript框架&#xff0c;其核心概念之一就是虚拟DOM&#xff08;Virtual DOM&#xff09;。在本篇文章中&#xff0c;我们将深入探讨Vue中虚拟DOM的概念&#xff0c;并讨论为什么它在前端开发中如此重要…

小白水平理解面试经典题目_二维数组类LeetCode 2966 Divide Array【排序算法实现】

2966 将数组划分为具有最大差值的数组 小白渣翻译&#xff1a; 给定一个大小为 n 的整数数组 nums 和一个正整数 k 。 将数组分成一个或多个大小为 3 的数组&#xff0c;满足以下条件&#xff1a; nums 的每个元素都应该位于一个数组中。一个数组中任意两个元素之间的差异小…

读分布式稳定性建设指南文档

最近还是在做一些和稳定性建设相关的事情&#xff0c;找到一份《分布式稳定性建设指南》文档&#xff0c;摘抄了其中的重点&#xff0c;以便后续回顾方便&#xff0c;一直没上传好资源&#xff0c;我之后再试试&#xff0c;原文内容质量非常高。 大家可以先看一级目录即可&…

c#cad 创建-圆(二)

运行环境 vs2022 c# cad2016 调试成功 一、代码说明 这段代码是一个AutoCAD插件&#xff0c;用于在模型空间中创建一个圆形。 首先&#xff0c;我们需要定义一个命令类CreateCircleCommand&#xff0c;并在命名空间CreateCircleInCad中声明。 在CreateCircleCommand类中&a…