【代码随想录】Day67哈希表:力扣242,383,1,349,202,454,15,18
创始人
2024-05-02 00:46:27
0

目录

基础知识

哈希表

哈希函数

2.哈希碰撞

常见的哈希结构(三种)

数组

集合set

映射map

经典题目

数组作为哈希表

例题:力扣242 已完成

例题:力扣383 已完成

例题:力扣49

例题:力扣438

set作为哈希表

例题:力扣349 已完成

例题:力扣202 已完成

例题:力扣350

map作为哈希表

例题:力扣1 已完成

例题:力扣454 已完成

双指针方法

例题:力扣15 已完成

例题:力扣18 已完成

遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

基础知识

哈希表

1.哈希表是根据关键码的值而直接进行访问的数据结构

2.哈希表都是用来快速判断一个元素是否出现集合里

eg.只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。

将学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数

哈希函数

把学生姓名直接映射为哈希表上的索引,然后就可以查询索引下标快速直到这位同学是否再这所学校里了。

同哟hashcode把名字转化为数值,hashcode是通过特定编码方式,可将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

1.为了保证映射出来的索引数值都落在哈希表上,再次对数值做一个取模操作

2.哈希碰撞

解决几个同名的学生映射到哈希表的同一个位置

①拉链法:

小李1和小李2再索引1的位置发生了冲突,发生冲突的元素都被存储在链表中,这样就能通过索引找到小李1和小李2了。

*数据规模是dataSize,哈希表的大小为tableSize

 #选取适当的哈希表的大小,既不会因为数组空值浪费大量内存,不会因为链表太长在查找上浪费太多时间

②线性探测法:

**tableSize大于dataSize,依靠哈希表中的空位解决碰撞问题

常见的哈希结构(三种)

数组

集合set

优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::set红黑树有序O(log n)O(log n)
std::multiset红黑树有序O(logn)O(logn)
std::unordered_set哈希表无序O(1)O(1)

1.std::set

底层实现:红黑树,key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加

2.std::multiset

底层实现:红黑树,key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加

3.std::unordered_set

底层实现:哈希表

映射map

映射底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::map红黑树key有序key不可重复key不可修改O(logn)O(logn)
std::multimap红黑树key有序key可重复key不可修改O(log n)O(log n)
std::unordered_map哈希表key无序key不可重复key不可修改O(1)O(1)

经典题目

数组作为哈希表

例题:力扣242 已完成

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

class Solution {
public:bool isAnagram(string s, string t) {int hash[26];//初始化为0for(int i=0;i<26;i++){hash[i]=0;}//分别求长度int slen=s.length();int tlen=t.length();for(int i=0;i

思路:①题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。

②定一个数组叫做record,大小为26 就可以了,初始化为0,因为字符a到字符z的ASCII也是26个连续的数值。

③遍历 字符串s的时候,只需要将 s[i] - 'a' 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了。

④同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。

record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。

例题:力扣383 已完成

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

class Solution {
public:bool canConstruct(string ransomNote, string magazine) {/*暴力解法for(int i=0;imagazine.length()){return false;}for(int i=0;i

思路:

① 用一个长度为26的数组还记录magazine里字母出现的次数。

②用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。

例题:力扣49

思路:

例题:力扣438

思路:

set作为哈希表

例题:力扣349 已完成

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

class Solution {
public:vector intersection(vector& nums1, vector& nums2) {/*set的写法unordered_set res_set;//存放结果,用set的目的是去重unordered_set nums_set(nums1.begin(),nums1.end());//存放num1的数据for(int num:nums2){//发现nums2的元素在nums_set中出现过if(nums_set.find(num)!=nums_set.end()){res_set.insert(num);//插入}}//返回结果return vector(res_set.begin(),res_set.end());*///数组的写法unordered_setres_set;//存放结果,用set对结果去重int hash[1005]={0};//默认值为0for(int num:nums1){ //nums1中出现的字母在hash中记录hash[num]=1;}for(int num:nums2){//nums2中出现的话,res记录if(hash[num]==1){res_set.insert(num);}}return vector(res_set.begin(),res_set.end());}
};

思路:unordered_set

①输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序。

②如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

例题:力扣202 已完成

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

class Solution {
public://取数值各个位上的单数之和int getSum(int n){int sum=0;while(n){sum+=(n%10)*(n%10);n=n/10;}return sum;}bool isHappy(int n) {unordered_set resset;while(1){int sum=getSum(n);if(sum==1){return true;}//检测这个数之前是否出现过//如果这个数出现过,证明已经进入了循环,直接返回falseif(resset.find(sum)!=resset.end()){return false;}else{resset.insert(sum);//将sum数放进原来的集合里面,用下次判断}n=sum;//更新n的值}}
};

思路:

使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。

判断sum是否重复出现就可以使用unordered_set。

例题:力扣350

思路:

map作为哈希表

例题:力扣1 已完成

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

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

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

class Solution {
public:vector twoSum(vector& nums, int target) {//1.map用来存放遍历过的元素,在遍历数组时询问集合中是否出现过这个元素//2.key用来存放元素,value用来存放下标std::unordered_mapmap;for(int i=0;isecond,i};}//如果没有找到匹配对,九八访问过的元素和下标存在map中map.insert(pair(nums[i],i));}return{};//如果没找到就返回空}
};

思路:

①map用来做什么:用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下表,这样才能找到与当前元素相匹配的

②map中的key和value分别表示什么:数组中的元素作为key,有key对应的就是value,value用来存下标

例题:力扣454 已完成

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

class Solution {
public:int fourSumCount(vector& nums1, vector& nums2, vector& nums3, vector& nums4) {
unordered_map umap;key:a+b的数值,value:a+b数值出现的次数
for(int a:nums1){for(int b:nums2){umap[a+b]++;}
}
int count=0;//统计a+b+c+d=0出现的次数
//遍历C和D,找到如果如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来
for(int c:nums3){for(int d:nums4){if(umap.find(0-(c+d))!=umap.end()){count+=umap[0-(c+d)];}}
}
return count;}
};

思路:

①定义unordered_map,key放ab的和,value放ab和出现的次数

②遍历大A和B数组,统计两个数组元素之和,和出现的次数,放到map中

③定义遍历count,统计a+b+c+d=0出现的次数

④遍历CD数组,如果0-(c+d)在map中出现过,用count把map中key对应的value统计出来

⑤返回统计值count

双指针方法

用来解决三数、四数之和问题

例题:力扣15 已完成

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

class Solution {
public:vector> threeSum(vector& nums) {vector> result;sort(nums.begin(),nums.end()); //由于不返回数组下标 所以能进行排序 打乱顺序也不影响什么for(int i=0;i0){return result;}// 正确去重a方法if(i>0&&nums[i]==nums[i-1]){ //i>0保证数组不会越界continue;}int left=i+1;//left在当前头的右边第一个int right=nums.size()-1;//最后一个开始while(right>left){if(nums[i]+nums[left]+nums[right]>0){//值更大 缩小right=right-1;}else if(nums[i]+nums[left]+nums[right]<0){//值更小 扩大left=left+1;}else{result.push_back(vector{nums[i],nums[left],nums[right]});//储存满足条件的三元组while(right>left&&nums[right]==nums[right-1]) right--;//右边while(right>left&&nums[left]==nums[left+1]) left++;//左边//找到答案 双指针同时收缩left++;right--;}}}return result;}
};

思路:

①数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

②如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

③如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

④a的去重

⑤b、c的去重

例题:力扣18 已完成

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。

class Solution {
public:vector> fourSum(vector& nums, int target) {vector> result;sort(nums.begin(),nums.end());//第一层循环for(int i=0;itarget&&nums[i]>=0){break;}//第一层去重if(i>0&&nums[i]==nums[i-1]){continue;}//第二层循环for(int j=i+1;jtarget&&nums[i]+nums[j]>=0){break;}//第二层去重if(j>i+1&&nums[j]==nums[j-1]){continue;}int left=j+1;int right=nums.size()-1;while(right>left){if((long long) nums[i] + nums[j] + nums[left] + nums[right] > target){right--;}else if((long long) nums[i] + nums[j] + nums[left] + nums[right] < target){left++;}else{result.push_back(vector{nums[i],nums[j],nums[left],nums[right]});// 对nums[left]和nums[right]去重while(left

思路:

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,

相关内容

热门资讯

安卓充值系统怎么换成苹果系统,... 你是不是也和我一样,对安卓系统爱得深沉,但又忍不住对苹果系统的流畅和独特功能心动不已?别急,今天就来...
小米安卓切换苹果系统,探索全新... 你知道吗?最近有个大新闻在科技圈里炸开了锅,那就是小米手机用户竟然开始尝试将安卓系统切换到苹果系统。...
老电脑改装安卓系统,改装安卓系... 你那台老电脑是不是已经服役多年,性能越来越不给力了?别急,今天就来教你怎么给它来个华丽变身——改装成...
安卓组态系统是什么,安卓组态系... 你有没有听说过安卓组态系统?这可是个在科技圈里悄悄走红的小秘密哦!想象你手中拿着一部安卓手机,轻轻一...
安卓系统恢复饮食app,助您重... 你是不是也和我一样,手机里装了各种APP,但有时候感觉胃里像是被小怪兽抓挠,食欲不振,体重也跟着“涨...
安卓子系统不能运行,安卓子系统... 最近我的安卓手机有点儿闹脾气呢!你有没有遇到过这种情况:手机里某个子系统突然不能正常运行了?别急,让...
塞班系统转安卓教程,轻松实现跨... 你有没有想过,把你的老塞班手机升级到安卓系统,让它焕发第二春呢?这可不是什么难事,只要跟着我一步步来...
原生安卓系统电视推荐,解锁智能... 亲爱的电视迷们,你是不是已经厌倦了那些花哨却不够实用的智能电视?想要一台纯粹、原生、性能卓越的安卓系...
平板系统重装 安卓,安卓系统安... 亲爱的读者,你是不是也和我一样,对平板电脑的安卓系统重装充满了好奇和期待呢?想象当你手中的平板焕然一...
安卓系统下谷歌play,谷歌P... 你有没有发现,手机里的安卓系统里有个神奇的地方,那就是谷歌Play商店。它就像一个巨大的宝藏库,里面...
旧安卓系统直播软件,探索直播新... 你有没有发现,尽管智能手机更新换代的速度飞快,但有些旧安卓系统上的直播软件,竟然还能在江湖上混得风生...
车载安卓系统安装主题,个性化定... 你有没有发现,车载安卓系统的主题安装,简直就像给爱车换装一样,瞬间让车内氛围大变样!今天,就让我带你...
手机安卓系统测评软件,安卓系统... 你有没有发现,手机里的安卓系统越来越智能了?为了帮你挑选出最适合你的手机,今天就来聊聊那些超实用的安...
安卓系统是否可以越狱,越狱的可... 你有没有想过,你的安卓手机是不是也能像iPhone那样,摆脱束缚,自由自在地越狱呢?这可是不少安卓用...
安卓系统里办公软件,轻松管理 你有没有发现,随着智能手机的普及,安卓系统里的办公软件越来越丰富了呢?想象你正坐在办公室的椅子上,手...
安卓凤凰系统 触屏,引领安卓触... 你知道吗?最近手机界可是掀起了一股热潮,那就是安卓凤凰系统的新触屏技术。这可不是一般的升级,而是像凤...
安卓开发主板刷系统,轻松实现系... 你有没有想过,你的安卓手机或者平板,是不是也有时候表现得有点“懒洋洋”的?别急,今天就来给你支个招—...
安卓9退回旧系统,回顾与反思 你有没有发现,你的安卓手机最近有点不对劲?是不是突然间觉得卡顿了,或者某些功能突然消失了?别急,这可...
华为安卓系统易受攻击,易受攻击... 你知道吗?最近在科技圈里,关于华为安卓系统的安全问题可是引起了不小的波澜呢!咱们就来聊聊这个话题,看...
安卓双系统单定位,精准定位新体... 你有没有想过,手机里装两个系统,却只显示一个定位信息,这到底是啥操作呢?今天,就让我带你一探究竟,揭...