关于hash table的一些基础知识

首先,一般哈希表都是用来快速判断一个元素是否出现集合里。 他的核心思想就是空间换时间。哈希表一般有三种结构:

  • 数组:数组也可以作为哈希表使用,但是缺点是数组的大小在定义时就不可改变,所以适合数据有限的情况下使用,比如242.有效的字母异位词就可以使用。但数组作为哈希表使用时有一个好处,就是不用经过哈希函数运算得到映射,所以在增加元素时的速度要比另外两种快。

  • set:set在c++中有三种容器,但只有unordered_set的底层实现使用的是hash table。下面给出三种set容器的对比:

    (图片来自卡哥的代码随想录)
    可以看到setmultiset中的元素都是有序的,而unordered_set顾名思义其中的元素是无序的,并且unordered_set的增删查的效率都是O(1)。下面看一下unordered_set在c++中常用的函数:

    • 初始化:
    //一般常用的有有三种初始化的方式
    unordered_set<int> set1; //空的set
    unordered_set<int> set2(set1) //拷贝构造
    unordered_set<int> set3(set1.begin(),set1.end()) //使用迭代器构造,这里的迭代器也可以是vector
    unordered_set<int> set4(nums,nums+5) //使用数组构造
    unordered_set<int> set5 {1,2,3,4,5} //直接构造
    
    • 常用函数:
    unordered_set<int> set {1,2,3,4,5};
    //查找:find和count
    set.find(1) //find函数返回的是一个迭代器变量即元素的位置
    set.count(1) //count函数返回的是0/1,0代表无,1代表有
    //插入:insert,一般常用的有三种插入方式
    set.insert(6) //直接插入元素,返回pair<unordered_set<int>::iterator, bool>,插入位置和是否成功
    set.insert(set.end(),7) //指定位置插入,返回指向插入元素的迭代器
    set.insert(set1.begin(),set1.end()) //范围插入
    //删除:erase
    set.erase(1); //直接删除元素
    set.erase(set.find(1)); //输入的是迭代器
    set.erase(set.find(1),set.find(1)+1); //输入的是迭代器的范围
    

    参考此文:C++常用语法——unordered_set
    注意函数在输入参数数量变化下返回值的不同。

  • map:map是一种key-value的数据类型,与set一样有三种类型,分别是mapmultimapunordered_map,和set相似mapmultimap底层实现是红黑树,unordered_map底层是hash table,同样给出对比:

    (图片来自卡哥的代码随想录)
    其中mapunordered_map的区别主要就体现在底层实现。因为红黑树本质上是平衡二叉树吗,为了维护他的有序性,每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间。

    复习一下unordered_map的初始化和常用函数:

    //初始化
    unordered_map<char,int> umap; //char为key,int为value
    unordered_map<char,int> umap1{('a',1),('b',2)}; //隐式调用构造函数
    unordered_map<char,int> umap2(umap1); //拷贝构造
    unordered_map<char,int> umap3(umap1.begin(),umap1.end()); //迭代器初始化
    //添加元素
    umap['a']++; //直接使用访问数组的方式添加
    umap.insert(pair<char,int>('b',2)); //通过函数,pair对添加元素
    //查找
    umap.find('a'); //find获取元素的迭代器,如果没有返回umap.end()
    umap.count('a'); //count返回0/1,判断是否有元素
    //删除
    umap.erase('a'); //删除指定key
    umap.erase(umap.find('a')); //删除迭代器位置
    umap.erase(umap.find('a'),umap.find('a') + 1); //删除迭代器范围
    //获取key以及value
    auto it = umap.find('a');
    char key = it->first; //key
    int value = it->second; //value
    

    另外insert函数插入的数据不会覆盖已有value的key,而用数组方式插入的数据可以覆盖。

红黑树可以参考:【算法】红黑树(二叉树)概念与查询(一)的系列文章。

242.有效的字母异位词

本题需要统计重复字符出现的次数,并且次数有有意义的,所以想到用hash table。因为元素的长度是固定的,从a-z的26个英文字母,所以可以用数组做hash table。下面给出数组的代码:

class Solution {public:bool isAnagram(string s, string t) {int record[26] = {};for(int i = 0; i < s.size(); i++){record[s[i] - 'a']++;}for(int i = 0; i < t.size(); i++){record[t[i] - 'a']--;}for(int i = 0; i < 26; i++){if(record[i] != 0) return false;}return true;}
};

这里的s[i] - 'a'相当于做了一个hash映射。

或者用unordered_map也行:

class Solution {public:bool isAnagram(string s, string t) {int size1 = s.size();int size2 = t.size();if(size1!=size2)return false;unordered_map<char,int> m1;unordered_map<char,int> m2;for(int i = 0; i<size1;++i){m1[s[i]]++;}for(int i = 0; i<size2;++i){m2[t[i]]++;}if(m1==m2)return true;return false;}
};

349. 两个数组的交集

本题因为输出结果中的每个元素一定是 唯一 的,我们可以不考虑输出结果的顺序,并且不需要统计出现次数,记录即可,所以想到用unordered_set作为储存答案的容器。
思路大体就是首先使用unordered_set将一个数组存入进去,然后再跟另一个数组比对,如果满足条件则存入答案中。下面给出代码:

class Solution {public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {unordered_set<int> ans;unordered_set<int> set(nums1.begin(),nums1.end());for(auto it = nums2.begin(); it != nums2.end(); it++){if(set.count(*it)) ans.insert(*it);}return vector<int>(ans.begin(),ans.end());}
};

有几个细节:

  • set的遍历包括vector和map都可以用迭代器遍历
  • set的初始化这里使用vector的迭代器初始化
  • 迭代器取值操作*it可以获得其对应的value

因为本题把范围限定在了1 <= nums1.length, nums2.length <= 1000,所以用数组做hash table也是可以的:

class Solution {public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {unordered_set<int> ans;int hash[1005] = {0};for(int num : nums1){hash[num] = 1;}for(int num : nums2){if(hash[num] == 1){ans.insert(num);}}return vector<int>(ans.begin(),ans.end());}
};

记得使用set作为答案输出做去重操作。

202. 快乐数

本题的重点就是无限循环这几个字,当sum开始重复的时候,就代表进入了无限循环,那么这个数就不是快乐数,看到寻找重复的元素时,就想到用hash table,又因为这里是无序且不需要记录元素对应的下标或者出现次数,所以使用unordered_set即可。
代码如下:

class Solution {public:int get_sum(int n){int sum = 0;while(n){sum += (n % 10) * (n % 10);n = n / 10;}return sum;}bool isHappy(int n) {unordered_set<int> set;while(1) {int sum = get_sum(n);if (sum == 1) {return true;}if (set.count(sum)) {return false;} else {set.insert(sum);}n = sum;}}
};

首先写一个求和函数,然后主函数中选择一个while(1)的死循环。
这里if (set.count(sum))中的判断也可以用if (set.find(sum) != set.end())也是可以的。

1. 两数之和

都说这题是梦开始的地方,但一开始写这道题的时候也是有点懵逼的,只会暴力解,后来才想到用hash做,代码很短但是其实没有那么容易理解。大体思想就是遍历数组,在遍历数组的过程中,向map中插入新的元素,然后再遍历的时候要判断当前数组元素是否能在哈希表中找到满足条件的元素。代码如下:

class Solution {public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int,int> map;for(int i = 0; i < nums.size(); i++){if(map.find(target - nums[i]) != map.end()){return {map.find(target - nums[i])->second,i};}map.insert(pair<int,int>(nums[i],i));}return {};}
};

需要注意的是,要先判断,再向map中插入新元素。这里unordered_map中的key是数组中的元素,value是下标,因为unordered_mapfind函数的参数是key值。另外返回类型是vector的话,可以直接返回{}

代码随想录第六天 LeetCode 242、349、202、1 (哈希表)相关推荐

  1. [英雄星球六月集训LeetCode解题日报] 第七日 哈希表

    [英雄星球六月集训LeetCode解题日报] 第七日 哈希表 一. 442. 数组中重复的数据 1. 题目描述 2. 思路分析 3. 代码实现 二. 2068. 检查两个字符串是否几乎相等 1. 题目 ...

  2. 代码随想录(day04)-LeetCode:24、19、面试题02.07、142

    代码随想录:dayo4 1. [24]两两交换链表中的节点 虚拟头结点实现 递归实现 2.[19]**删除链表的倒数第N个节点** 双指针算法 3. 面试题[02.07]:链表相交 4.[142]环形 ...

  3. LeetCode 136. Single Number【哈希表/位运算/数学】简单

    本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12.由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止:由于LeetCode还在不断地创建新 ...

  4. leetcode - 141. 环形链表(哈希表)

    给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. 示例 1: 输入: ...

  5. day6代码随想录算法训练营| ● 242.有效的字母异位词 ● 349. 两个数组的交集 ● 202. 快乐数● 1. 两数之和

    ● 242.有效的字母异位词 用map的话效率应该会很低吧,特别是查询全部value class Solution { public:bool isAnagram(string s, string t ...

  6. [leetcode]1.两数之和 + 哈希表:梦开始的地方,英语的abandon

    方案一:暴力题解没什么可说的,当然不是面试官想看到的 复杂度分析 时间复杂度:O(N^2),其中 N是数组中的元素数量.最坏情况下数组中任意两个数都要被匹配一次.空间复杂度:O(1). 方案二:直白来 ...

  7. 代码随想录算法公开课!

    关注代码随想录的录友,基本都是跟着代码随想录一起刷题的. 目前代码随想录的内容是完全开放在代码随想录网站,Github,和Gitee上,同时也出版了<代码随想录>纸质版. 这套刷题顺序和题 ...

  8. 【LeetCode笔记】560. 和为K的子数组(Java、前缀和、哈希表)

    文章目录 题目描述 思路 & 代码 暴力法 O(n2n^2n2) 前缀和 + 哈希表 O(n) 二刷 题目描述 第一道前缀和题目- 思路 & 代码 暴力法 O(n2n^2n2) 固定一 ...

  9. 代码随想录算法训练营第六天|242. 有效的字母异位词,349. 两个数组的交集,202.快乐数,1. 两数之和

    242. 有效的字母异位词 力扣题目链接 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词. 示例 1: 输入: s = "anagram", t = ...

  10. 小白刷代码随想录day6 -- 242.有效的字母异位词,349.两个数组的交集,202快乐数,1.两数之和

    今天是刷代码随想录的day6.昨天day5元宵节休息日.今天开始了哈希表部分.对于哈希表的内容之前没有怎么学习过,所以今天的刷题主要以学习方法为主.待二刷的时候争取能够手撕代码! 哈希法 首先一个大前 ...

最新文章

  1. Could not apply the stored configuration for monitors 解决办法
  2. dji大疆机器人冬令营_2019RoboMaster高中生机器人冬令营火热进行中
  3. VC++连MySQL小记
  4. Java记录 -9- 面向对象之封装
  5. SPOJ D-query(莫队算法模板)
  6. 重磅! flutter视图局部更新
  7. 树莓派安装qq linux,在树莓派上安装Ubuntu Core
  8. MTK MT2503 11B讲义-01编译
  9. Neo4j的下载与安装
  10. TVS管、稳压管、肖特基二极管
  11. Junos CLI常用命令
  12. 汽车零部件开发的流程及项目管理--陈新春老师
  13. Ceph Recovery分析
  14. C#字节数组与字符串相互转换代码案例
  15. 通过爬虫爬取一些图片
  16. 求矩阵伪逆的matlab方法,手把手教学
  17. 数据预处理(上)之离群值处理、标准化
  18. 【QGIS插件安装】buildseg: QGIS plugin for building extraction
  19. MacOS的一些技巧
  20. Mootools.js实现js的类的方式

热门文章

  1. c语言1 2.5*3,若有如下变量定义并赋值:inta=1,b=2,c=3,k;float f=2.5,e;doubled=2.4,g;则下列符合C语言语法的...
  2. [渝粤教育] 九江职业技术学院 客户关系管理 参考 资料
  3. Prettier your project
  4. C#实现程序一次打开两个窗口,两个窗口分别放置在两个屏幕
  5. 从0开始的技术美术之路(美术篇)(二)角色设计基础
  6. 攻防世界mfw--详细笔记
  7. 无纸化测评系统计算机考试试题,2015年9月计算机二级《Access》无纸化试卷(1)
  8. Ecshop各个页面文件介绍,主要文件功能说明
  9. 鸿蒙系统研究第一步:从源码构建系统镜像
  10. TP-LINK_A5CA路由器重设wifi密码