首页 > 编程语言 >C++ 哈希表的总结与例题

C++ 哈希表的总结与例题

时间:2023-03-14 20:32:44浏览次数:40  
标签:index return 哈希 map int C++ key 例题


文章目录

  • ​​C++STL​​
  • ​​哈希表​​
  • ​​设计哈希集合​​
  • ​​设计哈希映射​​
  • ​​哈希集合​​
  • ​​例题一:只出现一次的数字​​
  • ​​例题二:快乐数​​
  • ​​哈希映射​​
  • ​​例题一:两数之和​​
  • ​​例题二:两个列表的最小索引总和​​
  • ​​例题三:字符串中的第一个唯一字符​​
  • ​​设置键​​
  • ​​例题一:字母异位词分组​​
  • ​​例题二:寻找重复的子树​​

C++STL

c++的STL为我们提供了哈希集合和哈希映射。

哈希集合

  1. set

set基于红黑树实现,红黑树具有自动排序的功能,因此set内部所有的数据,在任何时候,都是有序的。

  1. unordered_set

unordered_set基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存,无自动排序功能。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash函数对key进行映射到不同区域进行保存。

哈希映射

  1. map

1、优点:
(1)有序性,这是map结构最大的有点,其元素的有序性在很多应用中都会简化很多的操作。
(2)红黑树,内部实现一个红黑树使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高。
2、缺点:空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间。
3、适用处:对于那些有顺序要求的问题,用map会更高效一些。

2.unordered_map

1、优点:因为内部实现了哈希表,因此其查找速度非常的快。
2、缺点:哈希表的建立比较耗费时间
3、适用处:对于查找问题,unordered_map 会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map

本文主要使用unordered_set与unordered_map。

哈希表

unordered_set的使用:

//1. 创建哈希集合
unordered_set<int> s;
//2. 插入元素
s.insert(1);
s.insert(5);
s.insert(4);
s.insert(9); //注意: unordered_set无自动排序功能!!!
//3. 统计元素个数
s.count(4);
//4. 查询元素
auto it = s.find(4);//注意:返回此元素位置的迭代器
//5. 删除元素
s.erase(9);

unordered_map的基本操作:

//1. 创建哈希集合
unordered_map<int,int> s;
//2. 插入元素 使用这两种方式
s.insert(make_pair(1, 2));
s.insert(make_pair(2, 5));
s[4] = 9;
s[6] = 10;
//3. 统计元素个数
s.count(4);
//4. 查询元素
auto it = s.find(4);
//5. 删除元素
s.erase(9);
//6. unordered_map存储的是一个pair类型;
//pair的first指向键,second指向值,pair的first和second构成了哈希映射的《键值对》
for (auto& x : s)
{
//访问每一个元素
cout << x.first << " " << x.second << endl;
}
//可以直接访问键值对
cout << s[7] << s[6] << endl;

设计哈希集合

不使用任何内建的哈希表库设计一个哈希集合(HashSet):

实现 MyHashSet 类:
void add(key) 向哈希集合中插入值 key 。
bool contains(key) 返回哈希集合中是否存在这个值 key 。
void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。

#define MAX_NUM 100000

class MyHashSet {
private:
vector<int> arr[MAX_NUM];
public:
MyHashSet() {

}

void add(int key) {
int index = getindex(key); //获取集合索引
int pos = getpos(key, index); //获取桶位置
if (pos < 0)
{
arr[index].emplace_back(key);
}
}

void remove(int key) {
int index = getindex(key);
int pos = getpos(key, index);
if (pos >= 0)
{
//存在此元素,则删除
arr[index].erase(arr[index].begin() + pos);
}
}

bool contains(int key) {
int index = getindex(key);
int pos = getpos(key, index);
if (pos >= 0)
{
return true;
}
return false;
}
private:
int getindex(int key)
{
return key % MAX_NUM;
}
int getpos(int key, int index)
{
int n = arr[index].size();
for (int i = 0; i < n; i++)
{
if (arr[index][i] == key)
{
return i;
}
}
return -1;
}
};

设计哈希映射

不使用任何内建的哈希表库设计一个哈希映射(HashMap)。

实现 MyHashMap 类:

MyHashMap() 用空映射初始化对象
void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的值 value 。
int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 -1 。
void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value 。

class M_MyHashMap {
private:
pair<int, int> elem;
vector<pair<int, int>> map[MAX_NUM];
public:
M_MyHashMap() {

}

void put(int key, int value) {
int index = getindex(key);
int pos = getpos(key, index);
if (pos < 0)
{
map[index].emplace_back(make_pair(key, value));
}
else
{
map[index][pos].second = value;
}
}

int get(int key) {
int index = getindex(key);
int pos = getpos(key, index);
if (pos < 0)
{
return -1;
}
else
{
return map[index][pos].second;
}
}

void remove(int key) {
int index = getindex(key);
int pos = getpos(key,index);
if (pos >= 0)
{
map[index].erase(map[index].begin() + pos);
}
}
private:
int getindex(int key)
{
return key % MAX_NUM;
}
int getpos(int key, int index)
{
int n = map[index].size();
for (int i = 0; i < n; i++)
{
if (map[index][i].first == key)
{
return i;
}
}
return -1;
}
};

哈希集合

例题一:只出现一次的数字

一个数组会出现重复的元素,找出那个只出现了一次的元素

int singleNumber(vector<int>& nums) {
unordered_set<int> s;
for (auto& x:nums)
{
if (s.count(x))
{
//已经存在,则出现两次,则删除此元素
s.erase(x);
}
else
{
//没有进入上面的循环,说明只出现了一次
s.insert(x);
}
}
//最后剩下的一定是只出现一次的,因为出现多次的都被删除了
return *s.begin();
}

例题二:快乐数

一个数字各位的数字平方后相加,等于1则是快乐数,如果进入无限循环则不是快乐数

int getnum(int num)
{
int sum=0;
while (num)
{
sum+=((num%10)*(num%10));
num/=10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> s;
//当 n 不等于 1 或者集合里没有出现此元素时,循环继续
while (n!=1 && !s.count(n))
{
//集合每次都存储n的当前值,判断是否有重复元素
s.insert(n);
n=getnum(n);
}
//循环结束,说明1. n=1 -> true 2.有重复元素 -> false
return n==1;
}

哈希映射

例题一:两数之和

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

vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> m;
int n=nums.size();
for (int i=0;i<n;i++)
{
//如果 目标元素减当前元素的结果存在,则哈希映射中存在此元素
if (m.count(target-nums[i]))
{
//返回当前下标 i 和对应相减结果的map中的下标
return {i,m[target-nums[i]]};
}
//映射进map
m[nums[i]]=i;
}
return {};
}

例题二:两个列表的最小索引总和

假设 Andy 和 Doris 想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示。

你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如果答案不止一个,则输出所有答案并且不考虑顺序。 你可以假设答案总是存在。

vector<string> findRestaurant(vector<string>& list1, vector<string>& list2) {
unordered_map<string,int> m;
int n1=list1.size();
for (int i=0;i<n1;i++)
{
m[list1[i]]=i;
}
int n2=list2.size();
vector<string> res;
int minIdx=INT_MAX;
for (int i=0;i<n2;i++)
{
if (m.count(list2[i]))
{
string str=list2[i];
//计算当前下标
int idx=i+m[str];
if (idx<minIdx)
{
//当前下标小于最小下标,则更新res中字符串的值和minIdx
res.clear();
res.push_back(str);
minIdx=idx;
}
else if (idx==minIdx)
{
//两个下标相等,则也是他们两个都喜欢的
res.push_back(str);
}
}
}
return res;
}

例题三:字符串中的第一个唯一字符

返回字符串中第一个只出现一次的字符的位置
abcdcbd : 返回 0

int firstUniqChar(string s) {
unordered_map<char,int> m;
for (auto& x:s)
{
//存储字符及其出现次数
m[x]++;
}
int n=s.size();
for (int i=0;i<n;i++)
{
if (m[s[i]]==1)
{
return i;
}
}
return -1;
}

设置键

例题一:字母异位词分组

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

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> m;
for (auto& x:strs)
{
string s=x;
sort(s.begin(),s.end());
m[s].push_back(x);
}
vector<vector<string>> res;
for (auto& x:m)
{
res.push_back(x.second);
}
return res;
}

例题二:寻找重复的子树

给你一棵二叉树的根节点 root ,返回所有 重复的子树 。
对于同一类的重复子树,你只需要返回其中任意 一棵 的根结点即可。
如果两棵树具有 相同的结构 和 相同的结点值 ,则认为二者是 重复 的。

C++ 哈希表的总结与例题_哈希映射

class Solution {
private:
unordered_map<string,TreeNode*> m;
unordered_set<TreeNode*> s;
public:
vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
func(root);
return {s.begin(),s.end()};
}
string func(TreeNode* root)
{
if (root==nullptr)
{
return "";
}
else
{
string str=to_string(root->val)+"("+
func(root->left)+")("+func(root->right)+")";
auto it=m.find(str);
if (it!=m.end())
{
//不存在于m中,s添加
s.insert(it->second);
}
else
{
m[str]=root;
}
return str;
}
}
};


标签:index,return,哈希,map,int,C++,key,例题
From: https://blog.51cto.com/u_15744744/6121102

相关文章

  • 以下是一个使用C++实现HTTP文件下载的简单示例,其中使用了C++ 11的标准库和Boost库:
    #include<iostream>#include<fstream>#include<boost/asio.hpp>usingboost::asio::ip::tcp;intmain(){try{boost::asio::io_serviceio_se......
  • C++ 基础部分 个人笔记
    本人菜鸟,个人学习笔记,如有错误还请指教C++模板是什么C++模板是一种基于类型参数化的编程技术,使用模板可以使得程序员编写独立于具体数据类型的通用代码。通过参数化类......
  • C++_一些重要的编译参数
    1.-g编译带调试信息的可执行文件#-g告诉g++产生可供GDB使用的调试信息。g++-gtest.cpp-otest2.-O[n]优化源代码-O:同时减小代码的长度和执行时间,其效果等价于......
  • 【多线程】C++11多线程(简约但不简单) 原创
    【多线程】C++11多线程(简约但不简单) 目录​ ​一、简单使用​​​ ​1、线程参数​​​ ​2.类成员函数做为线程入口​​​ ​3.join:等待线程执......
  • C/C++班级成绩管理系统[2023-03-13]
    C/C++班级成绩管理系统[2023-03-13]4.5班级成绩管理系统题目描述对一个有N个(>=10)学生的班级,每个学生有M门(>=5)课程。该系统实现对班级成绩的录入、显示、修改、排序......
  • c/c++指针从浅入深介绍——基于数据内存分配的理解(上)
    c/c++指针从浅入深介绍——基于数据内存分配的理解(上)本文是对自我学习的一个总结以及回顾,文章内容主要是针对代码中的数据在内存中的存储情况以及存储中数值的变化来......
  • 【C++踩坑】成员函数内的静态变量
    个人记录用,一直以为成员函数内的静态变量不同实例是分别存储的。事实上是所有实例共享。#include<iostream>classTest{public:voidtest(){staticinti=......
  • 基于哈希的索引和基于树的索引的区别
    1、hash索引仅满足“=”、“IN”和“<=>”查询,不能使用范围查询因为hash索引比较的是经常hash运算之后的hash值,因此只能进行等值的过滤,不能基于范围的查找,因为经过hash算......
  • c++之模版类
    一.模板类的定义函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体;意义:对于功能完全一样,只是参数类型不同的函数,能写一段通用代码是......
  • subprocess,哈希,日志模块
    hashlib模块:#1.先确定你要使用的加密方式:md系列,sha系列md5=hashlib.md5()#指定加密方式#2.进行明文数据的加密data='hello123456'md5.update(b'hell......