哈希表算法题

news2024/11/30 5:34:58

目录

题目一——1. 两数之和 - 力扣(LeetCode)

1.1.暴力解法1

1.2.暴力解法2 

1.2.哈希表解法 

题目二——面试题 01.02. 判定是否互为字符重排 - 力扣(LeetCode) 

 题目三——217. 存在重复元素 - 力扣(LeetCode)

题目四——219. 存在重复元素 II - 力扣(LeetCode) 

题目五—— 49. 字母异位词分组 - 力扣(LeetCode)


 

我们这里不讲哈希表的原理那些,如果想要学习其他更多的,去其他地方,这里只讲实用的东西

  • 哈希表是什么?

就是一个存储数据的容器。

  • 哈希表有什么用?

可以快速查询某个元素,时间复杂度是O(1).

  • 什么时候使用哈希表?

我们需要频繁的查询一个数据的时候,我们就得想到使用哈希表。

判断某个元素只出现一次的时候,我们就得想到使用哈希表。

虽然二分查找也很快,但是它的使用条件太苛刻了。

  • 怎么使用哈希表?
  1. 任何高级语言都有哈希表的容器,像我们的C++的unorder_map
  2. 其次我们可以使用数组模拟哈希表(比如大小为26的数组,每个元素都可以代表哈希表的值,其次只要元素是比较小的整数的情况,我们也可以使用数组模拟哈希表)

其实我们在滑动窗口算法-CSDN博客就已经使用过哈希表了!感兴趣的可以去看一下

  • unordered_map的常用成员函数——count成员函数

在C++的std::unordered_map中,count成员函数用于查找特定键(key)在哈希表中的出现次数。然而,对于std::unordered_map来说,一个键最多只能出现一次(即映射到一个值),因此count函数实际上只会返回0或1。

  • 如果键存在于哈希表中,count返回1。
  • 如果键不存在于哈希表中,count返回0。

题目一——1. 两数之和 - 力扣(LeetCode)

 

1.1.暴力解法1

这个直接两个for循环零帧起手。不必多讲。

  1. 固定一个数
  2. 往右边遍历查询
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 创建一个空的整数数组result,用于存储找到的索引
        vector<int> result;
        
        // 使用两层循环遍历数组nums,外层循环变量i从0开始,内层循环变量w从i+1开始
        for(int i=0; i<nums.size(); i++) { // 外层循环,遍历数组中的每一个元素
            for(int w=i+1; w<nums.size(); w++) { // 内层循环,从i的下一个元素开始遍历,避免重复计算i和i的组合
                // 如果当前两个元素的和等于目标值target
                if(nums[i] + nums[w] == target) {
                    // 将外层循环的索引i添加到结果数组result中
                    result.push_back(i);
                    // 将内层循环的索引w添加到结果数组result中
                    result.push_back(w);    
                }
            }
        }
        
        // 返回找到的结果数组
        return result; 
    }
};

 

1.2.暴力解法2 

我们依然是先固定一个数,但是我们不再是往后面遍历寻找另外一个数了,我们这次是往前遍历

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        
        for(int i=1; i<nums.size(); i++) { 
            for(int w=i-1;w>=0; w--) {              
                if(nums[i] + nums[w] == target) {
                    result.push_back(i);    
                    result.push_back(w);    
                }
            }
        }
        // 返回找到的结果数组
        return result; 
    }
};

 

1.2.哈希表解法 

暴力枚举为什么慢?因为每个数都被遍历了好多遍。如果说我们能保证每个元素都能被遍历一次,甚至不用所有数,那是不是快很多了??

哈希表就能做到

  1. 哈希表的键存该数组元素的值,哈希表的值存该数组元素的下标
  2. 从左边往右边遍历的过程中
  3. 我们要在哈希表里面找的有没有元素的值是target-nums[i]的
  4. 这个时候再将对应数组元素nums[i]丢入哈希表
class Solution 
{
public:
    std::vector<int> twoSum(std::vector<int>& nums, int target) 
    {
        // 创建一个哈希表,用于存储数组元素的值和对应的索引
        // 键是数组元素的值,值是该元素在数组中的索引
        std::unordered_map<int, int> hash;
        
        // 遍历数组nums
        for(int i = 0; i < nums.size(); i++)
        {
            // 计算当前元素需要的配对值,即target减去当前元素的值
            int x = target - nums[i];
            
            // 检查哈希表中是否已经存在需要的配对值
            // 如果存在,说明找到了两个数的和等于target
            // 此时,hash[x]是配对值的索引,i是当前元素的索引
            if(hash.count(x)) 
            {
                // 返回一个包含两个索引的数组
                return {hash[x], i};
            }
            
            // 将当前元素的值和索引存入哈希表
            // 这样,在后续的遍历中就可以通过查找哈希表来快速判断是否存在需要的配对值
            hash[nums[i]] = i;
        }
        
         return {-1, -1}; //这个只是为了照顾编译器
    }
};

 

题目二——面试题 01.02. 判定是否互为字符重排 - 力扣(LeetCode) 

 这题不就是判断两个字符串里面的字符种类,个数是不是一样吗?

我们用哈希表来记录每种字符的个数,如果两个字符串的哈希表不一样,则说明不是字符重排。

但是,哈希表的实现方式有两种

  1. unordered_map
  2. 数组

我将会提供两种实现方式

unordered_map版本

class Solution {
public:
    bool CheckPermutation(string s1, string s2) {
        // 如果两个字符串的长度不相等,它们不能是彼此的排列,直接返回false
        if(s1.length()!=s2.length()) {
            return false;
        }
        
        bool res=true;  // 初始化结果变量为true,假设它们是彼此的排列
        
        // 使用两个unordered_map来存储每个字符串中字符的出现次数
        unordered_map<char,int> hash1;
        unordered_map<char,int> hash2;
 
        // 遍历s1,统计每个字符的出现次数
        for(char x:s1) {
            hash1[x]++;
        }
        // 遍历s2,统计每个字符的出现次数
        for(char x:s2) {
            hash2[x]++;
        }
 
        // 遍历s1中的每个字符(因为我们已经检查了长度,所以s1和s2长度相同)
        for(int i=0;i<s1.length();i++) {
            // 检查当前字符在两个字符串中的出现次数是否相同
            // 如果不相同,说明它们不是彼此的排列,设置res为false并跳出循环
            if(hash1[s1[i]]!=hash2[s1[i]]) {
                res=false;
                break;
            }
        }
        // 返回最终的结果
        return res;
    }
};

数组模拟版本 

class Solution {
public:
    bool CheckPermutation(string s1, string s2) {
        // 如果两个字符串的长度不相等,它们不能是彼此的排列,直接返回false
        if(s1.size() != s2.size()) return false;
        
        // 定义一个长度为26的数组,用于统计小写字母a到z在每个字符串中的出现次数
        // 初始化数组为0,表示开始时没有字符被统计
        int hash[26] = { 0 };
        
        // 遍历第一个字符串s1,统计每个字符的出现次数
        // 使用字符的ASCII码值减去'a'的ASCII码值,得到0-25的索引,表示a-z
        for(auto ch : s1) {
            hash[ch - 'a']++;
        }
        
        // 遍历第二个字符串s2,对每个字符的出现次数进行递减操作
        // 如果递减后的次数小于0,说明s2中某个字符的数量超过了s1中该字符的数量,
        // 因此s1和s2不能是彼此的排列,返回false
        for(auto ch : s2) {
            hash[ch - 'a']--;
            if(hash[ch - 'a'] < 0) return false;
        }
        
        // 如果遍历完s2后,没有返回false,说明s1和s2中每个字符的出现次数都相同,
        // 因此它们是彼此的排列,返回true
        return true;
    }
};

 

 题目三——217. 存在重复元素 - 力扣(LeetCode)

 这道题目其实有很多解法,我只想讲哈希表版本

就是使用哈希表记录每个元素的出现次数,如果哪个元素的出现次数大于1,就直接返回true就好了

class Solution {
public:
    bool containsDuplicate(std::vector<int>& nums) {
        std::unordered_map<int, int> hash;
        for (int num : nums) {
            // 直接增加计数,并检查是否已经大于1
            if (++hash[num] > 1) {
                // 如果计数大于1,说明找到了重复元素
                return true;
            }
        }
        // 遍历完整个数组都没有找到重复元素
        return false;
    }
};

 

题目四——219. 存在重复元素 II - 力扣(LeetCode) 

 

解决该问题需要我们快速定位到两个信息: 

  1. 两个相同的元素;
  2. 这两个相同元素的下标。

因此,我们可以使⽤「哈希表」,令数组内的元素做key 值,该元素所对应的下标做val 值,将 「数组元素」和「下标」绑定在⼀起,存⼊到「哈希表」中。

        思考题: 将 如果数组内存在⼤量的「重复元素」,⽽我们判断下标所对应的元素是否符合条件的时候,需要将不 同下标的元素作⽐较,怎么处理这个情况呢?

答:这⾥运⽤了⼀个「⼩贪⼼」。

我们按照下标「从⼩到⼤」的顺序遍历数组,当遇到两个元素相同,并且⽐较它们的下标时,这两个下标⼀定是距离最近的,因为:

  • 如果当前判断符合条件直接返回 true ,⽆需继续往后查找。
  • 如果不符合条件,那么前⼀个下标⼀定不可能与后续相同元素的下标匹配(因为下标在逐渐变 ⼤),那么我们可以⼤胆舍去前⼀个存储的下标,转⽽将其换成新的下标,继续匹配。 
class Solution 
{
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) 
    {
        // 使用 unordered_map 来存储每个数字最近出现的索引
        unordered_map<int, int> hash;
        
        // 遍历数组 nums
        for(int i = 0; i < nums.size(); i++)
        {
            // 检查当前数字 nums[i] 是否已经在哈希表中
            if(hash.count(nums[i]))
            {
                // 如果在哈希表中找到了相同的数字,检查索引差是否小于等于 k
                if(i - hash[nums[i]] <= k) 
                {
                    // 如果索引差小于等于 k,说明找到了符合条件的两个数字,返回 true
                    return true;
                }
            }
            
            // 更新哈希表中当前数字的最新索引
            hash[nums[i]] = i;
            
            // 注意:原代码中的 14, 15, 16, 17 行是空的,这里没有实际的代码需要解释或修改
        }
        
        // 如果遍历完整个数组都没有找到符合条件的两个数字,返回 false
        return false;
    }
};

 

题目五—— 49. 字母异位词分组 - 力扣(LeetCode)

这个题目其实我们可以 

互为字⺟异位词的单词有⼀个特点:

  • 将它们「排序」之后,两个单词应该是「完全相同」的。

所以,我们可以利⽤这个特性,将单词按照字典序排序,如果排序后的单词相同的话,就划分到同⼀ 组中。

这时我们就要处理两个问题:

  1. 排序后的单词与原单词需要能互相映射;
  2. 将排序后相同的单词,「划分到同⼀组」;

利⽤语⾔提供的「容器」的强⼤的功能就能实现这两点:

  1. 将排序后的字符串( string )当做哈希表的key值
  2. 将字⺟异位词数组( string数组 )当成val值

定义⼀个「哈希表」即可解决问题。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 使用 unordered_map 存储排序后的字符串作为键,原始字符串的向量作为值
        unordered_map<string, vector<string>> hash;

        // 遍历字符串数组
        for (auto& s : strs) {
            // 创建一个临时字符串,用于存储排序后的结果
            string tmp = s;
            // 对临时字符串进行排序,这样字母异位词将具有相同的排序后字符串
            sort(tmp.begin(), tmp.end());

            // 将排序后的字符串作为键,将原始字符串添加到对应的值向量中
            // 这样就实现了将字母异位词分组存储
            hash[tmp].push_back(s);
        }

        // 准备一个向量,用于存储最终的结果
        vector<vector<string>> ret;

        // 遍历哈希表,将每个值向量(即一组字母异位词)添加到结果向量中
        // 使用传统的first和second成员访问方式遍历哈希表
        for (auto& elem : hash) {
            string x = elem.first;          // 键(排序后的字符串)
            vector<string> y = elem.second; // 值(包含原始字符串的vector)

            // 将值向量y添加到结果向量ret中
            ret.push_back(y);
        }

        // 返回最终结果
        return ret;
    }
};

 

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

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

相关文章

Galaxy预测比特币期权活跃交易将持续至2027年,特朗普执政中期

随着比特币及其相关产品的交易量不断增加&#xff0c;加密货币市场的期权交易也迎来了前所未有的活跃。Galaxy Digital的交易团队指出&#xff0c;贝莱德IBIT ETF期权在美国股市的交易量已经创下了新纪录。首日交易量高达353,716份合约&#xff0c;几乎与2012年Facebook期权上线…

Java-GUI(登录界面示例)

简述&#xff1a; 步骤&#xff1a; (1)构造界面(将组件对象加入容器对象,注意&#xff1a;应设定对容器对象的布局策略&#xff09; (2)为界面加入事件响应处理(如单击按钮&#xff09; 实现&#xff1a; 两种方式实现&#xff0c;只有用户名为"admin"且密码为…

屏幕分辨率|尺寸|颜色深度指纹

一、前端通过window.screen接口获取屏幕分辨率 尺寸 颜色深度&#xff0c;横屏竖屏信息。 二、window.screen c接口实现&#xff1a; 1、third_party\blink\renderer\core\frame\screen.idl // https://drafts.csswg.org/cssom-view/#the-screen-interface[ExposedWindow ] …

【论文阅读】三平面相关与变体

文章目录 1. 【CVPR2023】Tri-Perspective View for Vision-Based 3D Semantic Occupancy Prediction动机可视化方法Pipeline 2. 【2023/08/31】PointOcc: Cylindrical Tri-Perspective View for Point-based 3D Semantic Occupancy Prediction动机&#xff08;针对雷达点云、与…

Java - JSR223规范解读_在JVM上实现多语言支持

文章目录 1. 概述2. 核心目标3. 支持的脚本语言4. 主要接口5. 脚本引擎的使用执行JavaScript脚本执行groovy脚本1. Groovy简介2. Groovy脚本示例3. 如何在Java中集成 Groovy4. 集成注意事项 6. 与Java集成7. 常见应用场景8. 优缺点9. 总结 1. 概述 JSR223&#xff08;Java Spe…

定时/延时任务-ScheduledThreadPoolExecutor的使用

文章目录 1. 概要2. 固定速率和固定延时2.1 固定速率2.2 固定延时 3. API 解释3.1 schedule3.2 固定延时 - scheduleWithFixedDelay3.2 固定速率 - scheduleWithFixedDelay 4. 小结 1. 概要 前三篇文章的地址&#xff1a; 定时/延时任务-自己实现一个简单的定时器定时/延时任…

什么是sfp,onu,​为什么PON(​俗称“光猫”​)模块使用SC光纤接口

在现代网络设备中&#xff0c;我们经常会看到SFP或SFP接口的身影&#xff0c;这些接口有时被简称为光口&#xff0c;但这个称呼并不严谨。有些厂商则称之为多功能口或多用途口&#xff0c;然而这对于不了解的人来说可能还是一头雾水。SFP&#xff0c;即Small Form-Factor Plugg…

005 MATLAB符号微积分

前言&#xff1a; 在MATLAB中&#xff0c;数值与符号的主要区别在于它们的处理方式和应用场景 数值计算适用于实际的数值计算问题&#xff0c;如矩阵运算、数据分析等。符号计算适用于符号推导、公式化简和符号解析&#xff0c;如理论物理和工程计算。 01 符号对象 1.基本符…

深入实践:从零开始掌握GPT的应用开发

1. 为什么选择GPT&#xff1f; GPT&#xff08;Generative Pre-trained Transformer&#xff09;是当下最具影响力的语言生成模型之一&#xff0c;适用于生成文本、分析语言情感、翻译、多任务对话等多种场景。相比传统算法和模型&#xff0c;GPT有以下显著优势&#xff1a; …

WRF-Chem模式安装、环境配置、原理、调试、运行方法;数据准备及相关参数设置方法

大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果&#xff0c;同时气象因素是控制大气污染的关键自然因素。大气污染问题既是局部、当地的&#xff0c;也是区域的&#xff0c;甚至是全球的。本地的污染物排放除了对当地造成严重影响外&#xff0c;同时还会在…

开源项目:纯Python构建的中后台管理系统

来源&#xff1a;Python大数据分析 费弗里 大家好我是费老师&#xff0c;目前市面上有很多开源的「中后台管理系统」解决方案&#xff0c;复杂如「若依」那种前端基于Vue&#xff0c;后端基于Java的框架&#xff0c;虽然其提供了较为完善的一整套前后端分离权限管理系统解决方…

汽车免拆诊断案例 | 2017款捷豹F-PACE车发动机偶尔怠速不稳

故障现象  一辆2017款捷豹F-PACE车&#xff0c;搭载2.0 L GTDi发动机&#xff0c;累计行驶里程约为16万km。车主反映&#xff0c;车辆组合仪表上发动机故障灯点亮&#xff08;图1&#xff09;&#xff0c;且发动机偶尔怠速不稳。 图1 发动机故障灯点亮 故障诊断 接车后试车…

SQL进阶技巧:非等值连接--单向近距离匹配

目录 0 场景描述 1 数据准备 2 问题分析 ​编辑 ​编辑 3 小结 数字化建设通关指南 0 场景描述 表 t_1 和表 t_2 通过 a 和 b 关联时&#xff0c;有相等的取相等的值匹配&#xff0c;不相等时每一 个 a 的值在 b 中找差值最小的来匹。 表 t_1&#xff1a;a 中无重复值…

泷羽sec-云技术

基础之云技术 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec…

element ui select绑定的值是对象的属性时,显示异常.

需要声明 value-key"value",如果还不行可能是数据类型不一致数字0和字符串0是不一致的. el-select v-model"value" clearable placeholder"Select" value-key"value" style"width: 240px"><!-- <el-option v-for&…

【ChatGPT大模型开发调用】如何获得 OpenAl API Key?

如何获取 OpenAI API Key 获取 OpenAI API Key 主要有以下三种途径&#xff1a; OpenAI 官方平台 (推荐): 开发者用户可以直接在 OpenAI 官方网站 (platform.openai.com) 注册并申请 API Key。 通常&#xff0c;您可以在账户设置或开发者平台的相关页面找到申请入口。 Azure…

沸点 | 嬴图Powerhouse全面发布:从用户视角看嬴图实时图数据库的成长与价值

嬴图Powerhouse&#xff0c;直译过来就是能量站、动力站&#xff0c;它是嬴图自2019年发布高密度并行图计算引擎以来的一个里程碑&#xff0c;包括对整个产品架构的一个彻底革新&#xff0c;为大量复杂数据进行计算、分析和存储提供了快速和强大的动力支持。目前嬴图的用户正在…

深入理解计算机系统,源码到可执行文件翻译过程:预处理、编译,汇编和链接

1.前言 从一个高级语言到可执行程序&#xff0c;要经过预处理、编译&#xff0c;汇编和链接四个过程。大家可以思考下&#xff0c;为什么要有这样的过程&#xff1f; 我们学习计算机之处&#xff0c;就应该了解到&#xff0c;计算机能够识别的只有二进制语言&#xff08;这是…

Gitee markdown 使用方法(持续更新)

IPKISS 获取仿真器件的名称 引言正文标题换行第一种------在行末尾手动键入两个空格第二种------额外换行一次&#xff0c;即两行中间留一个空行 缩进与反缩进代码块行内代码添加图片添加超链接 加粗&#xff0c;倾斜&#xff0c;加粗倾斜 引言 有些保密性的文件或者教程&…

Element UI 打包探索【1】

目录 第一个命令 第二个命令 node build/bin/iconInit.js node build/bin/build-entry.js node build/bin/i18n.js node build/bin/version.js 总结 最近在接触组件库的项目&#xff0c;所以特意拿来Element UI借鉴学习一下&#xff0c;它算是做前端的同学们离不开的一…