【基础算法总结】哈希表/set/map篇

【基础算法总结】哈希表/set/map篇

目录

一,哈希表简介

哈希思想是算法中一个十分重要的思想,体现的是一种映射关系,而哈希表就是基于哈希思想实现的存储数据的容器。哈希表的作用是快速查找某个元素,时间复杂度为O(1),时间复杂度为O(n)
使用哈希一般有两种方式:
(1)STL中的 unordered系列容器。
(2)用数组模拟简易哈希表。这种情况一般用于处理字符串中的"字符"或是当数据范围很小的时候使用。

下面将通过若干道题目进一步体会哈希表的使用

二,算法原理和代码实现

1.两数之和

在这里插入图片描述


在这里插入图片描述

算法原理:

(1) 如果我们可以事先将数组内的元素和下标绑定在⼀起存⼊哈希表中,然后直接在哈希表中查找每⼀个元素的 target - nums[i] ,就能快速的找到⽬标和的下标
(2) 这⾥有⼀个⼩技巧,我们可以不⽤将元素全部放⼊到哈希表之后,再来⼆次遍历(因为要处理元素相同的情况)。⽽是在将元素放⼊到哈希表中的同时,直接来检查表中是否已经存在当前元素所对应的⽬标元素(即 target - nums[i] )。如果它存在,那我们已经找到了对应解,并⽴即将其返回。⽆需将元素全部放⼊哈希表中,提⾼效率
(3) 因为哈希表中查找元素的时间复杂度是 O(1) ,遍历⼀遍数组的时间复杂度为 O(N) ,因此可以将时间复杂度降到 O(N)

代码实现:

class Solution { public: vector<int>twoSum(vector<int>& nums,int target){ unordered_map<int,int> hash;// 存元素和对应的下标for(int i =0; i < nums.size(); i++){// 检查在该元素之前是否存在一个数等于target-nums[i]int x = target - nums[i];if(hash.count(x))return{hash[x], i}; hash[nums[i]]= i;// 不存在就插入}return{-1,-1};}};

349.两个数组的交集

在这里插入图片描述


在这里插入图片描述

算法原理:

解法1:使用哈希表
定义两个哈希表 hash1 和 hash2 ,把两个数组都扔进哈希表中进行去重,遍历 hash1 中的每个元素,在 hash2 中查找是否存在,若存在就是交集。把交集存入数组里即可

代码实现:

class Solution { public: vector<int>intersection(vector<int>& nums1, vector<int>& nums2){// 去重 unordered_set<int>s1(nums1.begin(), nums1.end()); unordered_set<int>s2(nums2.begin(), nums2.end());// 遍历s1,在s2中查找是否存在,若存在就是交集 vector<int> ret;for(auto x : s1)if(s2.find(x)!= s2.end()) ret.push_back(x);return ret;}};
解法2:使用 set 排序+去重。
这种解法不仅能找出交集,也能找到差集。
把两个数组都扔进 set 容器达到排序+去重的效果,再依次比较两个 set 里的元素,谁小谁++,相等时再同时++,此时相等的元素就是交集,较小的元素就是差集,直到其中一个 set 走完了,另一个 set 剩下的元素也是差集

代码实现:

class Solution { public: vector<int>intersection(vector<int>& nums1, vector<int>& nums2){// 排序+去重 set<int>s1(nums1.begin(), nums1.end()); set<int>s2(nums2.begin(), nums2.end());// 依次比较,谁小谁++,相等的就是交集,再同时++auto it1 = s1.begin(), it2 = s2.begin(); vector<int> ret;while(it1 != s1.end()&& it2 != s2.end()){if(*it1 <*it2)++it1;elseif(*it1 >*it2)++it2;else ret.push_back(*it1),++it1,++it2;}return ret;}};

面试题01.02.判断是否互为字符重排

在这里插入图片描述


在这里插入图片描述

算法原理:

(1) 当两个字符串的⻓度不相等的时候,是不可能构成互相重排的,直接返回 false
(2) 如果两个字符串能够构成互相重排,那么每个字符串中各个字符出现的次数⼀定是相同的。因此,我们可以分别统计出这两个字符串中各个字符出现的次数,然后逐个⽐较是否相等即可。所以可以选择哈希表来统计字符串中字符出现的次数

代码实现:

class Solution { public: bool CheckPermutation(string s1, string s2){// 长度不相等,直接返回falseif(s1.size()!= s2.size())return false;int hash1[26]={0}, hash2[26]={0};// 先统计s1和s2中每个字符出现得到次数for(auto ch : s1) hash1[ch -'a']++;for(auto ch : s2) hash2[ch -'a']++;// 再比较两个哈希表中每个字符出现的次数是否相同for(char ch ='a'; ch <='z'; ch++)if(hash1[ch -'a']!= hash2[ch -'a'])return false;return true;}};
优化:只使用一个哈希表
用一个哈希表先统计s1中每个字符出现的个数,再遍历s2时把s2中出现的字符在哈希表中-1。如果构成重排列,则最后哈希表中的个数都为0,否则不构成

代码实现:

class Solution { public: bool CheckPermutation(string s1, string s2){// 长度不相等,直接返回falseif(s1.size()!= s2.size())return false;int hash[26]={0};// 统计s1中字符出现的次数for(auto ch : s1) hash[ch -'a']++;// 再遍历s2,出现的字符-1for(auto ch : s2) hash[ch -'a']--;// 最后检查hash里是否都为0for(int i =0; i <26; i++)if(hash[i]!=0)return false;return true;}};

217.存在重复元素

在这里插入图片描述


在这里插入图片描述

算法原理:
这道题和第一题的做法基本类似。

分析⼀下题⽬,出现⾄少两次的意思就是数组中存在着重复的元素,因此我们可以⽆需统计元素出现的数⽬。仅需在遍历数组的过程中,检查当前元素是否在之前已经出现过即可
因此我们可以利⽤哈希表,仅需存储数组内的元素。在遍历数组的时候,⼀边检查哈希表中是否已经出现过当前元素,⼀边将元素加⼊到哈希表中

代码实现:

class Solution { public: bool containsDuplicate(vector<int>& nums){ unordered_set<int> hash;for(auto x : nums)if(hash.count(x))return true;else hash.insert(x);return false;}};

219.存在重复元素II

在这里插入图片描述


在这里插入图片描述

算法原理:

这道题和上一题基本一样,只不过除了要两个元素相等之外,还需要判断相等元素的下标差的绝对值是否小于等于k。所以本题的哈希表要把元素值和对应下标绑定

细节问题:

如果数组内存在⼤量的重复元素,⽽我们判断下标差的绝对值不符合要求时,在插入相同元素的下标,会覆盖掉原来的下标,那结果还正确吗,怎么处理?
结果依然是正确的,可以大胆的覆盖掉!


原因:
我们按照下标从⼩到⼤的顺序遍历数组,当遇到两个元素相同,并且⽐较它们的下标时,这两个下标⼀定是距离最近的,因为:
(1) 如果当前判断符合条件直接返回 true ,⽆需继续往后查找。
(2) 如果不符合条件,那么前⼀个下标⼀定不可能与后续相同元素的下标匹配(因为下标在逐渐变大),那么我们可以大胆舍去前⼀个存储的下标,转⽽将其换成新的下标,继续匹配

代码实现:

class Solution { public: bool containsNearbyDuplicate(vector<int>& nums,int k){ unordered_map<int,int> hash;// 绑定元素和下标for(int i =0; i < nums.size(); i++){if(hash.count(nums[i])){if(abs(hash[nums[i]]- i)<= k)return true;} hash[nums[i]]= i;}return false;}};

692.前k个高频单词

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

算法原理:

解法:使用 map + 稳定排序函数
先用map统计出每个单词出现的次数,map中是按照单词的字典序排序的,所以还需要用排序函数对次数进行排降序,最后提取出前 k 个出现最多的字符串

魔鬼细节问题:

对次数再排序时有以下几个注意点:
(1)排序函数一定要使用稳定排序!算法库中的 sort 是不稳定的,但是 stable_sort 是稳定的。
因为如果使用不稳定的排序,则出现次数相同的字符串顺序又会被打乱(map 中已经是按单词的字典序排序的),不符合题意

(2) 使用排序函数之前要把 map 中的元素先导入 vector 中,再传递 vector 的迭代器给 stable_sort
因为stable_sort 只能传递随机迭代器,而 map 的迭代器是双向迭代器,vector 的迭代器是随机迭代器
(3) stable_sort 的第三个参数:一个可调用的比较函数和函数对象

代码实现:

class Solution {structkvcmp{ bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2){return kv1.second > kv2.second;}}; public: vector<string>topKFrequent(vector<string>& words,int k){// 统计每个字符串出现的次数 map<string,int> mapCnt;for(auto& s : words) mapCnt[s]++; vector<pair<string,int>>v(mapCnt.begin(), mapCnt.end());// 要用稳定排序对字符串出现的次数排降序stable_sort(v.begin(), v.end(),kvcmp());// 提取出前 k 个出现次数最多的单词 vector<string> ret;for(int i =0; i < k; i++) ret.push_back(v[i].first);return ret;}};

45.字母异位词分组

在这里插入图片描述


在这里插入图片描述

算法原理:

这道题要解决两个问题:
(1) 判断两个字符串是否是字母异位词。
我们可以使用上面题目中的做法用哈希表统计字符出现的个数再进行判断。这里用另一种更简洁的方法,排序,但是时间复杂度更高。把每个字符串按字典序进行排序,若是字母异位词,则排完序后字符串相等,否则不相等。
(2) 如何把字母异位词分组。
我们可以这样定义哈希表,把 key 定义为 string,把 value 定义为字符串数组 vector< string >

把每个字符串排序后再去哈希表中查找,如果存在,就把它绑定到对应的字符串数组中

代码实现:

class Solution { public: vector<vector<string>>groupAnagrams(vector<string>& strs){ 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;for(auto&[x, y]: hash) ret.push_back(y);return ret;}};

三,算法总结

通过上面的若干道题目可以发现,哈希容器/map/set是非常强大的,能够快速的查找数据是否存在,统计数据个数,去重,排序

Read more

基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的跌倒检测系统(千问+DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)

基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的跌倒检测系统(千问+DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)

项目摘要 本项目旨在设计并实现一个高效、智能且用户友好的基于多版本YOLO深度学习模型与SpringBoot Web框架的实时跌倒检测系统。随着全球老龄化社会的加速到来,老年人在日常生活中发生跌倒的风险日益增高,及时、准确地检测跌倒事件对于保障其生命安全与健康具有重大社会意义。传统监控或穿戴式设备存在隐私侵扰、用户体验不佳或漏报率高等局限。因此,本项目融合了当前前沿的计算机视觉技术与现代Web开发架构,构建了一个集智能分析、实时监控、数据管理与远程交互于一体的综合性解决方案。 系统的核心检测引擎采用了性能卓越的YOLO系列目标检测算法,并创新性地集成了YOLOv8、YOLOv10、YOLOv11及YOLOv12四种最新版本模型,为用户提供了灵活、可对比的算法选择,以适应不同的精度与速度需求。模型在精心标注的自定义数据集上进行训练与验证,该数据集包含 ‘fallen’(已跌倒)、‘falling’(正在跌倒)和‘stand’(站立/正常) 三个关键类别,共计3,888张图像(训练集3,594张,验证集294张),确保了系统对跌倒过程动态的精确识别能力。 系统后端采用SpringB

By Ne0inhk

node.js下载、安装、设置国内镜像源(永久)(Windows11)

目录 * node-v20.18.0-x64 * 工具 * 下载 * 安装 * 设置国内镜像源(永久) node-v20.18.0-x64 工具 系统:Windows 11 下载 1. 官网https://nodejs.org/zh-cn/download/package-manager 版本我是跟着老师选的node-v20.18.0-x64 下载完成 如图选择 Windows、x64、v20.18.0 (LTS),点击下载 安装 next、next、Install、Finish 自定义安装地址,我安到了F:Program Files odejs I accept打勾,next next

By Ne0inhk

Spring IOC 和 AOP 完全详解:从入门到精通

Spring IOC 和 AOP 完全详解:从入门到精通 📝 博客简介:本文将深入浅出地讲解Spring框架的两大核心特性——IOC(控制反转)和AOP(面向切面编程)。适合Spring初学者和准备面试的同学阅读。 🎯 知识目标:理解IOC和AOP的概念、实现机制、应用场景,并掌握依赖注入和反射的原理。 📋 目录 * 一、Spring IOC(控制反转)详解 * 二、Spring AOP(面向切面编程)详解 * 三、反射机制详解 * 四、实战代码演示 * 五、常见面试问题汇总 一、Spring IOC(控制反转)详解 1.1 什么是IOC? 📖 概念解释 IOC(Inversion of Control,控制反转) 是一种设计原则,

By Ne0inhk
黑马点评完整代码(RabbitMQ优化)+简历编写+面试重点 ⭐

黑马点评完整代码(RabbitMQ优化)+简历编写+面试重点 ⭐

简历上展示黑马点评 完整代码地址 微服务学成在线项目 前言 当初就是当作一个学习笔记和个人面试记录发的,没想到这么多人收藏浏览,还是感慨学Java的人确实多啊。 适合什么人看呢,我仅仅说说我个人的理解,因为我现在也是个经历秋招的双非学生。 1.初学者学习完Redis基础,想来个实战,黑马点评还是特别好的一个项目,基本包含了所有数据类型的运用和redis其他功能的扩展,这篇文章可以带你提炼重点,很好的走下流程。 2.但大部分人是冲着找实习和秋招去的,像我这种学历不高的秋招就不要写黑马点评了,即使包装,也会很容易看出来,我找实习的时候就被面试官问到这是不是黑马点评过,我们可以把其中的闪光点迁移到你找的其他项目中,比如缓存穿透雪崩击穿的解决方法,redisson分布式锁解决一人一单,这种在大多项目中都可以添加,自圆其说就行。 3.对于找实习的像大二,大三上的,想找个小厂试试手垂直向上升的,可以吃透它,面试官问你遇到的困难或者是你觉得难点,就可以重点讲一人一单这个解决方法和流程,越详细越好。 4.前提是大家不用直接用这套模板,太多人用了,这也是我从网上找的别人的,巧用AI让它改改项

By Ne0inhk