算法:哈希表

目录

题目一:两数之和

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

题目三:存在重复元素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;//如果没出现,返回falseif(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,然后判断是否小于0if(--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作为Valuestring 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.mzph.cn/pingmian/39075.shtml

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

相关文章

Hive On Spark语法

内层对象定义之特殊数据类型 Array DROP TABLE IF EXISTS test_table_datatype_array; CREATE TABLE test_table_datatype_array (ids array<INT> ) LOCATION test/test_table_datatype_array;SELECTnames,names[1]array(names[2],names[3])names[5],names[-1],array_c…

linux-内存映射MMAP-lseek-dup-fifo-通信-IO多路复用

1、内存映射MMap&#xff1a; DMA&#xff1a; 可以用*/[]取代read和write&#xff1b; 限制&#xff1a; 1、文件大小固定不能改变&#xff1b;&#xff08;ftruncate&#xff09; 2、只能是磁盘文件&#xff1b; 3、建立映射之前先open mmap函数&#xff1a; mmap第一个…

生产环境 CentOS 7 k8s v1.28.0离线部署

背景描述&#xff1a;CentOS 7 Kubernetes 离线部署 随着云计算和微服务架构的普及&#xff0c;Kubernetes&#xff08;K8s&#xff09;已经成为容器编排的标准工具。它能够自动化应用的部署、扩展和管理&#xff0c;使得开发和运维的工作更加高效和可靠。然而&#xff0c;在一…

腾讯开源高质量人类运动视频的框架;通过音频指令修改图像;利用YOLO分析网球视频;Gemma-2中文微调模型

✨ 1: MimicMotion MimicMotion 腾讯开源的通过姿态指导生成高质量任意长度人类运动视频的框架 MimicMotion 是一种可控视频生成框架&#xff0c;旨在生成高质量的任意长度人物动作视频&#xff0c;采用带有置信度的姿态引导&#xff0c;并通过区域损失放大来缓解图像失真。其…

C++视觉开发 三.缺陷检测

一.距离变换 1.概念和功能 距离变换是一种图像处理技术&#xff0c;用于计算图像中每个像素到最近的零像素&#xff08;背景像素&#xff09;的距离。它常用于图像分割、形态学操作和形状分析等领域。它计算图像中每个像素到最近的零像素&#xff08;背景像素&#xff09;的距…

LeetCode 176, 289, 437

目录 176. 第二高的薪水题目链接表要求知识点思路代码 289. 生命游戏题目链接标签简单版思路代码 进阶版思路代码 437. 路径总和 III题目链接标签思路代码 176. 第二高的薪水 题目链接 176. 第二高的薪水 表 表Employee的字段为id和salary。 要求 查询并返回 Employee 表…

苍穹外卖--sky-take-out(五)前端

大部分笔记都是写在语雀的&#xff0c;这是一次性从本人语雀复制过来的&#xff0c;可能结构有些错乱 基础创建 环境要求 node.js npm Vue CLI 创建前端工程 使用vue ui命令创建 项目结构 启动项目 打开命令行窗口 快捷键ctrlj 或者 运行 输入&#xff1a;npm run ser…

010-GeoGebra基础篇-动态验证三角形外接圆的圆心是否可以位于三角形的外部

接下来我们将进行一些稍微高级一点操作&#xff0c;一边学习新东西的同时&#xff0c;也开始对数学、物理等内容的研究。 目录 一、项目截图二、涉及内容三、问题设置1. 问题提出2. 验证方案 三、做图步骤1. 绘制定点A、B&#xff1b;2. 绘制动点C&#xff1b;&#xff08;1&am…

万界星空科技铜管加工行业MES系统解决方案

一、行业背景与挑战 随着铜管加工行业的快速发展&#xff0c;传统的管理模式已难以满足日益增长的生产需求。为满足市场的高效率、高质量、低成本要求&#xff0c;企业急需一套智能化的管理系统来提升生产效率、优化资源配置和确保产品质量。因此&#xff0c;我们针对铜管加工行…

常用的限流算法有哪些?你听说过几种?

限流&#xff0c;就是指限制流量请求的频次。 在高并发情况下&#xff0c;它是一种保护系统的策略&#xff0c;避免了在流量高峰时系统崩溃&#xff0c;造成系统的不可用。 常见的限流算法有&#xff1a; 计数器限流算法滑动窗口限流算法漏桶限流算法令牌桶限流算法 1. 计数器…

【Python程序开发系列】教你使用Docker部署一个简单的Python应用程序(案例+源码)

这是我的第313篇原创文章。 一、引言 Docker 对于程序员来说&#xff0c;其实和Git差不多&#xff0c;基本上属于一个必备工具。如果你想使用这个工具&#xff0c;你就必须安装这个应用工具&#xff0c;至于在不同操作系统上安装Docker的方式网上有很多教程&#xff0c;这里不…

每天五分钟深度学习:解决for循环效率慢的关键在于向量化

本文重点 上一节课程中,我们学习了多样本的线性回归模型,但是我们的伪代码实现中使用了大量的for循环,这样代码的问题是效率很低。为了克服这一瓶颈,向量化技术应运而生,成为提升程序执行效率、加速数据处理速度的重要手段。 向量化技术概述 向量化(Vectorization)是…

MySQL-核心知识要点

1、索引的数据结构-Btree BTree的优势&#xff1a; B树的内节点无data&#xff0c;一个节点可以存储更多的K-V对。在构造树时&#xff0c;需要的内节点会更少&#xff0c;那么树的层级也会越低。查询一条数据时&#xff0c;1. 扫描的层级低&#xff0c;扫描过的节点更少&…

最优化方法Python计算:标准型线性规划的轴转操作

标准型线性规划 { minimize c ⊤ x s.t. A x b x ≥ o ( 1 ) \begin{cases} \text{minimize}\quad\boldsymbol{c}^\top\boldsymbol{x}\\ \text{s.t.}\quad\quad\boldsymbol{Ax}\boldsymbol{b}\\ \quad\quad\quad\quad\boldsymbol{x}\geq\boldsymbol{o} \end{cases}\quad\quad…

【netty系列-04】反应堆模式的种类和具体实现

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

Vue3认识Vue插件

通常我们向Vue全局添加一些功能时&#xff0c;会采用插件的模式&#xff0c;它有两种编写方式: 对象类型:一个对象&#xff0c;但是必须包含一个install 的函数&#xff0c;该函数会在安装插件时执行;函数类型:一个function&#xff0c;这个函数会在安装插件时自动执行; 插件…

大数据平台之CDH

Clouderas Distribution Including Apache Hadoop (CDH) 是 Cloudera 提供的企业级 Hadoop 发行版&#xff0c;包含了 Hadoop 及其生态系统中的各种组件&#xff0c;并进行了优化和增强&#xff0c;适合在生产环境中使用。以下是 CDH 版本 Hadoop 的详细介绍&#xff1a; 概述…

数字化转型中,数字化如何重塑中小企业发展力?

引言&#xff1a;当前&#xff0c;我国中小微企业数字化转型处于“不平衡、不充分、不规范”阶段&#xff0c;普遍面临“不会转”“不能转”“不敢转”的困境。数字化转型可以帮助企业突破这些困境&#xff0c;实现更大的发展。更进一步&#xff0c;数字化转型是中小企业高质量…

学习笔记——动态路由——IS-IS中间系统到中间系统(基本概念)

二、IS-IS基本概念 1、IS-IS概述 IS-IS是ISO定义的OSI协议栈中的无连接网络服务(ConnectionLess Network Service&#xff0c;CLNS)的一部分&#xff0c;IS-IS是一种链路状态路由协议&#xff0c;IS-IS与OSPF在许多方面非常相似&#xff0c;例如&#xff0c;运行IS-IS协议的直…

4.BeanFactory

可以看出BeanFactory表面上只有getBean相关的方法。 实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能&#xff0c;都是由BeanFactory的实现类来实现的。&#xff08;DefaultListableBeanFactory&#xff09; DefaultListableBeanFactory管理单例对象DefaultSinglet…