算法:哈希表

目录

题目一:两数之和

题目二:判定是否互为字符重排

题目三:存在重复元素I

题目四:存在重复元素II

题目五:字母异位词分组


关于哈希表

哈希表就是存储数据的容器

哈希表的优势是:快速查找某个元素O(1)

所以当频繁查找某一个数时,就可以使用哈希表,这里提一点,在频繁查某个数时,也需要想到前面学过的二分算法,时间复杂度是O(logN),虽然比哈希表慢一些,但是哈希表的空间复杂度高,而logN级别的时间复杂度也是非常快的,所以能使用二分还是建议使用二分

关于哈希表的使用:

①使用容器
②使用数组模拟哈希表,此时下标是K,内容是V

例如字符串的字符的操作,数据范围很小的时候,可以使用数组模拟

之所以使用数组模拟而不直接使用容器,是因为用数组模拟速度比较快,相比较而言,如果使用容器,还得new一个容器,查找时还需要调用接口,也有消耗 


题目一:两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

需要注意,这道题要求一个元素不能重复在答案中出现

解法一:暴力解法

在我们前面所讲的暴力枚举方式,是固定一个数,再将这个数后面的所有数与该数的组合一一枚举出来,最终完成暴力枚举

这里采用相反的方向,先固定一个数,再与前面的数依次枚举,同样可以完成暴力枚举的操作

由于第一次使用这种查找前面数的暴力方式,所以写一下代码,便于理解,两层for循环,也是比较简单的:

class Solution 
{
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        for(int i = 0; i < nums.size(); i++)
        {
            for(int j = i - 1; j >= 0; j--)//从固定数的前面找符合要求的数
            {
                if(nums[i] + nums[j] == target) return {j, i};
            }
        }
        //照顾编译器
        return {0,0};
    }
};

解法二:哈希表优化

之所以在上面提到了第二种暴力枚举的思路,就是为了优化暴力枚举的方式,引入哈希表,具体说明如下:

数组是: [2,7,10,15],目标是17

先固定一个数2,再找2之前的数,由于2之前没有数,所以往下遍历

接着固定7,再找7之前的数,不符合,继续向下遍历

这时遍历到10了,按照之前暴力枚举的思路,将固定的数与它之前的数依次相加,从而顺利找到目标值,但是可以引入优化的思路,此时就体现哈希的思想了:

既然此时固定的数是10,目标值是17,那么就不需要向前遍历,依次与10相加,判断是否等于目标值,直接将 目标值 - 固定的数 = 17 - 10 = 7,也就是在固定的数前面需要找一个数7,那么这时引入哈希表,在固定前面的数时都放入哈希表中,所以此时需要找7,就不需要遍历前面的数了,直接使用哈希表查找,哈希表查找的时间复杂度为O(1),效率是非常高的,是典型的用空间换时间的算法,因为哈希表的空间复杂度是O(N)

这里说明一下,为什么需要引入第二种暴力枚举,来用哈希表进行优化呢,第一种暴力枚举的策略为什么不太好优化?

第一点:需要提前将元素放入哈希表

第一种暴力枚举的方式,是固定一个数,向后寻找,此时如果想用哈希表的策略,我们就需要提前将数组中的所有数放入哈希表中,才能够知道后面有没有数等于 (target - 该数),比第二种方式差,第二种是边遍历边插入

第二点:很容易误判自己

由于刚开始将所有元素都放入了哈希表中,假设数组是[1,2,4,5],目标值是8,当遍历到 4 这个数时,哈希表中已经有全部的元素在了,此时我们需要找的数是 8 - 4 = 4,很容易误判导致自己找到自己,因为需要找的是4,自己本身也是4,所以这里还需要特殊判断一下找到的数字是否是本身,而上面说的第二种方式,是从固定的数前面找,不会找到自己的,因为都是判断过了,才会插入到哈希表中


所以在做题时,哈希表中,第一个存的是数,第二个存的是该数的下标

代码如下:

class Solution 
{
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        unordered_map<int, int> hash;//<nums[i], i>
        for(int i = 0; i < nums.size(); i++)
        {
            int ret = target - nums[i];//需要找的值
            if(hash.count(ret))
            {
                return {hash[ret], i};
            }
            hash[nums[i]] = i;
        }
        //照顾编译器
        return {0,0};
    }
};

题目二:判定是否互为字符重排

给定两个由小写字母组成的字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。

示例 1:

输入: s1 = "abc", s2 = "bca"
输出: true 

示例 2:

输入: s1 = "abc", s2 = "bad"
输出: false

这道题可以理解为:判断两个字符串重排列之后,所包含的字母是否相同,也就是判断两个字符串中包含的字母个数是否相同

所以很明显,就可以使用哈希表来统计出现的个数,使用容器unordered_map和数组模拟哈希表都能完成, 这里肯定是优先采用数组模拟的方法,因为数组的速度回快一些,并且容器的效率也不高,消耗也会比数组大一些,下面两种解法都列举:

解法一:使用unordered_map容器

先将第一个字符串的字符插入哈希表中,然后遍历另一个字符串,此时每遍历一个字符都判断是否在这个哈希表中,如果不在就return false,如果在value--,再在下面判断value是否为0,因为可能会出现多个相同字符,如果减到0了,就说明不对,return false即可

下面还可以优化一点,如果两个字符串长度都不同,那就不需要比较了,肯定是不同的

代码如下:

class Solution 
{
public:
    bool CheckPermutation(string s1, string s2) 
    {
        if(s1.size() != s2.size()) return false;
        unordered_map<char, int> hash(s1.size());
        for(auto& it : s1) hash[it]++;//插入哈希表中
        for(auto& it : s2)//遍历字符串2,与哈希表中元素比较
        {
            if(!hash.count(it)) return false;//如果没出现,返回false
            if(hash[it] == 0) return false;//如果出现多次,返回false
            --hash[it]; //每次出现value--
        }
        //走到这里还没有return,说明是相同的字符串
        return true;
    }
};

解法二:数组模拟

只需要开大小为26的数组即可,因为题目中只会出现小写字母

同样使用上述的方法,只需要一个数组,将第一个字符串插入数组中,然后遍历第二个字符串,当出现相同字符时,该位置的数--,如果发现减到负数,就返回false

同样可以加上优化,如果两个字符串长度都不同,那就不需要比较了,肯定是不同的

代码如下:

class Solution 
{
public:
    bool CheckPermutation(string s1, string s2) 
    {
        if(s1.size() != s2.size()) return false;
        int hash[26] = {0};
        for(auto& it : s1) hash[it - 'a']++;//插入数组对应位置中
        for(auto& it : s2)//遍历字符串2,与数组中元素比较
        {
            //先--value,然后判断是否小于0
            if(--hash[it - 'a'] < 0) return false;
        }
        //走到这里还没有return,说明是相同的字符串
        return true;
    }
};

题目三:存在重复元素I

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

示例 1:

输入:nums = [1,2,3,1]
输出:true

示例 2:

输入:nums = [1,2,3,4]
输出:false

示例 3:

输入:nums = [1,1,1,3,3,4,3,2,4,2]
输出:true

这道题就是判断是否会出现重复元素,比较简单

 可以使用上面的两数之和那道题的思路,当遍历到这个数时,找找这个数前面是否有数等于这个数,又因为并不需要关注下标,只需要关注是否出现,所以使用unordered_set即可

代码如下:

class Solution 
{
public:
    bool containsDuplicate(vector<int>& nums) 
    {
        unordered_set<int> hash(nums.size());
        for(int i = 0; i < nums.size(); i++)
        {
            if(hash.count(nums[i])) return true;
            hash.insert(nums[i]);
        }
        return false;
    }
};

题目四:存在重复元素II

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。

示例 1:

输入:nums = [1,2,3,1], k = 3
输出:true

示例 2:

输入:nums = [1,0,1,1], k = 1
输出:true

示例 3:

输入:nums = [1,2,3,1,2,3], k = 2
输出:false

这道题与上一道题解法基本相同,在上一题是否有重复元素的基础上,增加了条件,判断重复元素的下标之差是否小于等于K,所以这里的哈希表就需要统计下标了,所以就需要使用容器unordered_map了

同样,遍历到某个数时,先判断前面是否出现相同的数,如果出现,再下标之差是否小于等于K,如果都满足就return true,否则继续往后遍历,当遍历完还没有return,就说明没有符合条件的数,最后return false

代码如下:

class Solution 
{
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) 
    {
        unordered_map<int, int> hash(nums.size());
        for(int i = 0; i < nums.size(); i++)
        {
            //如果重复,则继续判断重复元素的下标是否满足条件
            if(hash.count(nums[i]) && ((i - hash[nums[i]]) <= k))
                return true;
            hash[nums[i]] = i;                
        }
        //走到这说明没有符合条件的数
        return false;
    }
};

题目五:字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

提示:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母

此题是给出了字符串数组,vector<string>,每个元素存储的是一个字符串,最终将每一个相同字母组成的字符串放在一起,最终返回

这道题就是深度考验我们对于哈希表容器中类型的使用,我们可以想到一种非常巧妙的方法,首先需要判断是否是字母异位词,可以用上面的哈希表的方式,先将第一个字符串放入哈希表,再遍历第二个字符串,是否与哈希表中出现的一样,但是这种方式不利于更好的解决下面的问题,所以这道题的策略是:

将题目的每一个字符串都按照ASCLL码排序,创建一个unordered_map<string, vector<string>>,然后遍历题目给的字符串数组,如果出现相同的就插入到unordered_map的value中,如果出现不同的就新创建一个插入

再遍历这个哈希表,将相同位置的vector<string>都插入到一起,最终返回即可

代码如下:

class Solution 
{
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) 
    {
        unordered_map<string, vector<string>> hash;
        //把所有字母异位词分组
        for(auto& it : strs)
        {
            //以排序后的tmp作为哈希表的Key,插入的it作为Value
            string tmp = it;
            sort(tmp.begin(), tmp.end());
            hash[tmp].push_back(it);
        }
        //结果提取出来
        vector<vector<string>> ret;
        //表示将哈希表的元素都提取出来,first存在x里,second存在y里
        for(auto& [x,y] : hash)
        {
            ret.push_back(y);
        }
        return ret;
    }
};

哈希表题目到此结束啦

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

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

相关文章

K8S 集群节点缩容

环境说明&#xff1a; 主机名IP地址CPU/内存角色K8S版本Docker版本k8s231192.168.99.2312C4Gmaster1.23.1720.10.24k8s232192.168.99.2322C4Gwoker1.23.1720.10.24k8s233&#xff08;需下线&#xff09;192.168.99.2332C4Gwoker1.23.1720.10.24 1. K8S 集群节点缩容 当集群中有…

ROS2使用Python开发动作通信

1.创建接口节点 cd chapt4_ws/ ros2 pkg create robot_control_interfaces --build-type ament_cmake --destination-directory src --maintainer-name "joe" --maintainer-email "1027038527qq.com" mkdir -p src/robot_control_interfaces/action touch…

【微服务】微服务之Feign 与 Ribbon

文章目录 强烈推荐引言优点Feign示例什么是Ribbon&#xff1f;Ribbon 的优点Netflix Feign 和 Ribbon整合Feign 与 Ribbon 的关系Feign 与 Ribbon 结合使用的示例配置文件&#xff08;application.yml&#xff09;说明&#xff1a; Feign 与 Ribbon 结合使用的应用场景1. 动态服…

XD3C03P1G、XD3C01N3F比例方向控制阀放大板

XD3A01N2G、XD3A03N2G、XD3C03N1F、XD3C03P1G、XD3C01N3F、XD3C03N2F、XD3C01P4G、XD3C03P3G、XD3C03N2F、XD3C03N2G、XDP3A01P1F、XDP3C03N2G、XDP3A03P3G、XDP3C01NAF、XDP3C03P6G、XDP3A03PAG、XDP3A03N3F液压比例方向阀是液压系统中的关键元件&#xff0c;用于实现对流量、…

C#/WPF 自制截图工具

在日常使用电脑办公时&#xff0c;我们经常遇到需要截图然后保存图片&#xff0c;我们往往需要借助安装截图工具才能实现&#xff0c;现在我们通过C#自制截图工具&#xff0c;也能够轻松进行截图。 我们可以通过C#调用WindousAPI来实现截图&#xff0c;实例代码如下&#xff1a…

Three.js 中的光照模型

Three.js 中的光照模型 Three.js 的一个伟大抽象就是统一了所有材质的光照模型, 无论 PBR 或者 Phong。都只用两个函数给全部囊括了。 就是 RE_Direct(直接反射) 和 RE_IndirectDiffuse(间接反射)。真正做到了大一统。下面以Phong为例,具体看一下如何落地。 省流版本: // 直接…

CSF视频文件格式转换WMV格式(2024年可用)

如果大家看过一些高校教学讲解视频的话&#xff0c;很可能见过这样一个难得的格式&#xff0c;".csf "&#xff0c;非常漂亮 。 用暴风影音都可以打开观看&#xff0c;会自动下载解码。 但是一旦我们想要利用或者上传视频的时候就麻烦了&#xff0c;一般网站不认这…

开放式耳机哪个品牌质量最好最耐用?2024热门红榜耳机真实测评

随着人们生活质量的提高&#xff0c;喜爱运动的小伙伴也越来越多了&#xff0c;开放式蓝牙耳机的佩戴舒适度与稳定性这两个优势在很多运动场景中可以为用户带来更好的使用体验。此外&#xff0c;我们的音频使用、通话、游戏等应用场景在不断拓宽&#xff0c;蓝牙耳机的使用时间…

qt可点击的QLabel

需求——问题与思路 使用wpf实现一个可点击的超链接label相当简单&#xff08;如下图&#xff09;&#xff0c;但是qt的QLabel不会响应点击事件&#xff0c;那就从QLabel继承一个类&#xff0c;然后在该类中重写mousePressEvent函数&#xff0c;并在该函数中对左键点击事件做响…

FPGA工程师有前途吗 ?FPGA崛起之路

全球 FPGA 市场规模犹如滚雪球般逐年扩大。 根据Gartner Group预测&#xff0c;2020-2026年全球FPGA市场规模从55.85亿美元增至96.9亿美元&#xff0c;年均复合增长率为9.6%。 众多国际知名科技企业&#xff0c;如赛灵思、Lattice等&#xff0c;纷纷加大在 FPGA 研发和应用方…

linux操作系统数据盘挂载目录home改到www

云服务器开通后安装宝塔面板&#xff0c;数据盘默认挂载在 /home目录&#xff0c;通常这个目录不是我们需要的&#xff0c;数据盘需要挂载更换到/www目录。 如图所示数据盘/dev/mapper/ao-home 挂载到/home目录 但是我们需要它挂载到/www目录 以下操作是将数据盘/dev/mapper/…

希尔排序的实现

引言 排序在我们生活中十分常见&#xff0c;无论是购物软件中的商品推荐还是名次、排名都与排序算法息息相关。希尔排序是排序中较快的一种&#xff0c;而希尔排序实现的基础是插入排序。 排序的实现 插入排序&#xff08;以升序为例&#xff09; 插入排序的原理是从第二个数…

非静压模型SWASH学习(8)——三维孤立波在锥形岛屿上的爬坡过程(Runup of solitary waves on a conical island)

三维孤立波在锥形岛屿上的爬坡过程&#xff08;Runup of solitary waves on a conical island&#xff09; 算例简介模型配置网格及参数设置网格与地形初始条件与边界条件数值求解方法输出设置模拟时间 波浪&#xff08;孤立波&#xff09;入射边界的时间序列.bnd文件模拟结果注…

基于OpenCV与Keras的停车场车位自动识别系统

本项目旨在利用计算机视觉技术和深度学习算法&#xff0c;实现对停车场车位状态的实时自动识别。通过摄像头监控停车场内部&#xff0c;系统能够高效准确地辨认车位是否被占用&#xff0c;为车主提供实时的空闲车位信息&#xff0c;同时为停车场管理者提供智能化的车位管理工具…

Python基础小知识问答系列-记录最后N个元素

1. 问题&#xff1a; 怎么复制变量内容&#xff1f; 进行可迭代的操作过程中&#xff0c;如何记录最后几次操作的内容&#xff1f; 2. 解决方式&#xff1a; 对于非数值类型的变量&#xff0c;复制变量内容时&#xff0c;使用"*"。 记录最后n个元素&#xff…

重大丨深中通道今通车!继港珠澳大桥后,三思再度点亮世界工程

6月30日下午3时&#xff0c;国家重大工程深中通道正式通车试运营&#xff0c;向世界再次展示中国智慧和基建实力。已承接过包括港珠澳大桥海底隧道在内2500多条隧道照明工程的上海三思电子工程有限公司&#xff0c;为这座超级工程提供了LED隧道照明、东西人工岛照明及显示、管理…

【力扣】赎金信

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 ​ 给你两个字符串…

私有云统一多云管理平台主要服务内容

私有云统一多云管理平台&#xff0c;作为企业IT架构现代化的关键组成部分&#xff0c;旨在为企业提供高效、灵活、安全的云计算资源管理解决方案。这类平台通过整合和优化不同云环境(包括私有云、公有云、混合云)的管理&#xff0c;帮助企业打破云孤岛&#xff0c;实现资源的统…

【MySQL备份】Percona XtraBackup增量备份实战篇

目录 1.前言 2.准备工作 2.1.环境信息 2.2.创建备份目录 2.3.配置/etc/my.cnf文件 2.4.授予root用户BACKUP_ADMIN权限 3.增量备份 3.1.第一步&#xff1a;全量备份 3.2.第二步&#xff1a;增量备份 3.3.第三步&#xff1a;再次增量备份 4.准备备份 4.1.准备全量备…

秋招Java后端开发冲刺——基础篇5(String集合)

一、String String类是Java中字符串操作类&#xff0c;位于java.lang包下String类型对象的底层使用字符数组char[]存储字符串&#xff0c;由final修饰且没有提供公共的修改方法&#xff0c;因此String对象是不可变的。常见方法 方法名作用trim()去掉字符串首尾空字符split(分…
最新文章