哈希表

          表: 存储数据 key –> value;

用表来存储数据结构的困难: 查找困难。一个一个key去比较去查找,效率不高。因此有了Hash算法加快查找;

将字符串的key,转成整数,使用整数找到对应的value;Hash算法将字符串转成整数,同样的Hash值的 key:value会放到一个集合里面,由于Hash能使得不同的字符串尽量有不同的整数值(仍然有重复); 将海量的数据,按照HASH值分成不同的集合,先找集合,再找key–>value,大大提高效率;

 散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key),adr = f(key)。查找时,根据这个确定的对应关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。我们把这种对应关系f称为散列函数,又称为哈希函数(Hash).按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表或哈希表(Hash table)。关键字对应的记录存储位置称为散列地址。

除留余数法

  为最常用的构造散列函数的方法。对于散列表长为m的散列函数公式为:f(key) = key mod p(p≤m)

这种方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。**散列表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。

链地址法:将所有关键字为同义词的记录存储在一个单链表中,这种链表叫做同义词子表,使用除留余数法,就不存在冲突的问题了,只是在链表中增加一个结点。


一、Map

映射是指两个集合之间的元素的相互对应关系。通俗地说,就是一个元素对应另外一个元素。比如一个姓名的集合 {“Tom”, “Jone”, “Marry”},班级集合{1, 2}。姓名与班级之间可以有如下的映射关系: class(“Tom”) = 1 , class(“Jone”) = 2 , class(“Marry”) = 1 
我们称其中的姓名集合为 关键字集合(key) , 班级集合为值集合(value) 。 在 C++ 中我们常用的映射是 map。

map的特点是增加和删除节点对迭代器的影响很小。对于迭代器来说,可以修改实值,而不能修改key。

1、构造一个映射

在C++中,我们构造一个 map 的语句为:

map<T1,T2> m;

这样我们定义了一个名为 m 的从 T1 类型到 T2 类型的映射。初始的时候 m 是空映射。

2、插入映射

插入有三种方法:

  1. 采用创建pair的形式插入pair<string, string>("string", "字符串")
  2. 采用make_pair的形式进行插入make_pair("apple", "苹果")
  3. 采用大括号的形式进行插入{ "left", "左边" }

通过 insert( ) 方法向集合中插入一个新的映射,参数是一个 pair 类型的结构。这里需要用到另外一个 STL 模板 -元组(pair)。

map<string, int> dict;  // {}
dict.insert(pair<string, int>("Tom", 1)); // {"Tom"->1}
dict.insert(pair<string, int>("Jone", 2)); // {"Tom"->1, "Jone"->2}dict.insert(pair<string, string>("string", "字符串"));//模板类型pair:构造了一个匿名对象插入到map
dict.insert(make_pair("apple", "苹果"));//模板函数make_pair:偷懒了,实际调的是pair
dict.insert({ "left", "左边" });
dict.insert({ "left", "剩余" });//插入不进去了,因为key值已经有了

3、访问映射

访问映射合,直接用 [] 就能访问。比如 dict[“Tom”] 就可以获取 “Tom” 的班级了。而这里有一个比较神奇的地方,如果没有对 “Tom” 做过映射的话,此时你访问 dict[“Tom”] ,系统将会自动为 “Tom” 生成一个映射,其 value 为对应类型的默认值。并且我们可以之后再给映射赋予新的值,比如 dict[“Tom”] = 3 ,这样为我们提供了另一种方便的插入手段。

    map<string, int> dict;  // {}dict["Tom"] = 1; // {"Tom"->1}dict["Mary"] = 1; // {"Tom"->1, "Mary"->1}printf("Mary is in class %d\n", dict["Mary"]);

4、查找关键字

count( ) :

在 C++ 中,如果你想知道某个关键字是否被映射过,你可以直接用 count( ) 方法。使用count,返回的是被查找元素的个数。如果有,返回1;否则,返回0。注意,map中不存在相同元素(Tom,Mary),所以返回值只能是1或0。

find()函数:

find函数,返回的是被查找元素的位置,没有则返回map.end()。注意:map.end()不指向最后一个元素,而指向最后一个元素再+1,当find不到输入的元素时候,返回迭代器就指向这个end() 。

int main(){map<string,int> test;test.insert(make_pair("test1",1));   //test["test1"]=1test.insert(make_pair("test2",2));   //test["test2"]=2map<string,int>::iterator it=test.find("test0");if(it==test.end())cout<<"test0 not found"<<endl;elsecout<<it->second<<endl;cout<<test.count("test1")<<endl;it=test.find("test1");if(it==test.end())cout<<"test1 not found"<<endl;elsecout<<it->second<<endl;

输出: test0 not found    1    1

5、遍历映射

通过迭代器可以访问映射中的每个映射,每个迭代器的 first 值对应key,second值对应value。

for (map<string, int>::iterator it = dict.begin(); it != dict.end(); ++it) cout << it->first << " is in class " << it->second << endl;

6、其他

erase 删除关键字
size 获取映射对个数
clear 清空std::map 就是以key来查找value而设计,根据key排序。

list=[5,14,34,22,39,5];

map<int, int> map1;for (int i=0; i<list.size(); i++){map1[i] = list[i];}for (map<int, int>::iterator i = map1.begin(); i != map1.end(); i++){cout << i->first << ' ' << i->second << endl;}if (map1.find(3) != map1.end()) {cout << "find key=" << map1.find(3)->first << ", value=" << map1.find(3)->second << endl;}if (map1.count(5) > 0) {cout << "count 5: " << map1.count(5) << endl;}

输出:
 0 5
1 14
2 34
3 22
4 39
5 5
find key=3, value=22
count 5: 1

二、unordered_map

std::unordered_map 就是以key来查找value而设计,不会根据key排序。其实现使用了哈希表。

 unordered_map<int, int> map;for (int i=0; i<list.size(); i++){map[i] = list[i];}cout << map[0] << endl;for (unordered_map<int, int>::iterator i = map.begin(); i != map.end(); i++){cout << i->first << ' ' << i->second << endl;}if (map.find(3) != map.end()) {cout << "find key=" << map.find(3)->first << ", value=" << map.find(3)->second << endl;}if (map.count(5) > 0) {cout << "find 5: " << map.count(5) << endl;}

输出:

5
5 5
4 39
3 22
2 34
1 14
0 5
find key=3, value=22
find 5: 1

  • count()返回要查找的key在map的所有key中的出现次数。因为此容器不允许重复,故count()只可能返回 1 或 0,即可判断此key是否存在。有返回1,无返回0。
  • unordered_map也有find方法,得到的对象是一个iterator,在unordered_map中,如果find()没找到要找的key,就返回和end()一样的iterator值。

对比:

unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。

unordered_map可类比于Python中的字典。其实现使用了哈希表,可以以O(1)的时间复杂度访问到对应元素,但缺点是有较高的额外空间复杂度。与之对应,STL中的map对应的数据结构是红黑树,红黑树内的数据时有序的,在红黑树上查找的时间复杂度是O(logN),相对于unordered_map的查询速度有所下降,但额外空间开销减小。

结论:如果需要内部元素自动排序,使用map,不需要排序使用unordered_map


二、集合

set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值。set不允许两个元素有相同的键值。C++的标准库中的集合支持高效的插入、删除和查询操作,这三个操作的时间复杂度都是 O(lgn),其中n是当前集合中元素的个数。如果用数组,虽然插入的时间复杂度是 O(1),但是删除合查询都是 O(n),此时效率太低。在C++中我们常用的集合是set。std::set 是基于hash表的,因此并不是顺序存储。

我们构造set集合的目的是为了快速的检索,不可直接去修改键值。可以先删后插。

1、构造一个集合

set<T> s;

这样我们定义了一个名为s的、储存T类型数据的集合,其中T是集合要储存的数据类型。初始的时候s是空集合。

2、插入元素

用 insert( ) 方法向集合中插入一个新的元素。注意如果集合中已经存在了某个元素,再次插入不会产生任何效果,集合中是不会出现重复元素的。

set<string> country;  // {}
country.insert("China"); // {"China"}
country.insert("America"); // {"China", "America"}

3、删除元素

country.erase("America"); // {"China"}

4、查找元素

  1. 想知道某个元素是否在集合中出现,你可以直接用 count( ) 方法--返回某个值元素的个数,如果集合中存在我们要查找的元素,返回 1 ,否则返回 0 。
  2. 另一种方法是:find()--返回一个指向被查找到元素的迭代器
set<int> set;for (int i=0; i<list.size(); i++){set.insert(list[i]);}for (auto i = set.begin(); i != set.end(); i++) {cout << *i << endl;}

输出:5 14 22 34 39

cout << set.count(5) << endl;
if(set.find(5) != set.end()) cout << *set.find(5) << endl; 

输出:     1    5

5、遍历集合

 for (set<string>::iterator it = country.begin(); it != country.end(); ++it) cout << (*it) << endl;

注意:在C++中遍历set是从小到大进行的。

6、其他

方法 功能

begin()       

返回set容器的第一个元素
erase 删除一个元素
end()      返回set容器的最后一个元素
size 获取元素的个数
clear 清空

from:https://www.cnblogs.com/omelet/p/6627667.html

unordered_set

std::unordered_set 是基于hash表的,因此并不是顺序存储。

 unordered_set<int> set;for (int i=0; i<list.size(); i++){set.insert(list[i]);}for (unordered_set<int>::iterator i = set.begin(); i != set.end(); i++) {cout << *i << endl;}cout << " find 39: " << *set.find(39) << endl;cout << "count 14:" << set.count(5) << endl;

输出:
22
39
34
14
5
 find 39: 39
count 14:1

三、总结:

unordered_map和map:

unordered_map存储机制是哈希表。unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的。
   map是红黑树,红黑树内的数据时有序的,里面的元素可以根据键进行自动排序。map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。

如果需要内部元素自动排序,使用map,不需要排序使用unordered_map

unordered_set和set:

unordered_set基于哈希表,是无序的
    set实现了红黑树的平衡二叉检索树的数据结构,插入元素时,它会自动调整二叉树的排列,把元素放到适当的位置,以保证每个子树根节点键值大于左子树所有节点的键值,小于右子树所有节点的键值;另外,还得保证根节点左子树的高度与右子树高度相等。平衡二叉检索树使用中序遍历算法,检索效率高于vector、deque和list等容器,另外使用中序遍历可将键值按照从小到大遍历出来。


set和map的区别:

1、set里面每个元素只存有一个key值,它支持高效的关键字查询操作,比如检查一个关键字是否在set中。如果这个key值之前存在的话就不插入。

set<int> s;s.insert(2);s.insert(1);s.insert(4);s.insert(5);s.insert(3);s.insert(5);s.insert(5);for (auto e : s)cout << e << " ";

打印出来的值为1 2 3 4 5。set容器自动对以上数据进行了排序,并且实现了去重。但是不能对set里的值进行修改,

set容器中的find查找效率高,因为底层是一个二叉搜索树,比要查找的值小就去左子树查找,反之则去右子树查找。

2、 map是一种key(键),value(值)的形式,用来保存键和值组成的集合,键必须是唯一的,但值可以不唯一。里面的元素可以根据键进行自动排序,由于map是key_value的形式,所以map里的所有元素都是pair类型。pair里面的first被称为key(键),second被称为value(值)。它可以通过关键字key查找映射关联信息value,同时根据key值进行排序。

set和map都以RBTree作为底层容器

set:

  • 所得元素的只有key没有value,value就是key
  • 不允许出现键值重复
  • 所有的元素都会被自动排序
  • 不能通过迭代器来改变set的值,因为set的值就是键

map:

  • map的值不作为键,键和值是分开的,所有元素都是键+值存在
  • 不允许键重复
  • 所有元素是通过键进行自动排序的
  • map的键是不能修改的,但是其键对应的值是可以修改的

from:https://blog.csdn.net/ETalien_/article/details/89439892

C++ set与map、unordered_map、unordered_set与哈希表相关推荐

  1. stl 基于哈希的map c++_关于哈希表,你该了解这些!

    (给算法爱好者加星标,修炼编程内功) 来源:代码随想录(本文来自作者投稿) 哈希表 首先什么是 哈希表,哈希表(英文名字为Hash table,国内也有一些算法书籍翻译为散列表,大家看到这两个名称知道 ...

  2. C++ 哈希表及unordered_set + unordered_map容器

    目录 一.unordered_set和unordered_map的使用 二.哈希结构 1.概念 2.哈希函数 (1) 哈希函数设计原则 (2)常见哈希函数 3.哈希冲突 4.哈希冲突解决 (1)闭散列 ...

  3. 【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程

    目录标题 1. 哈希表(unordered_map)和黑红树(map)简介以及初始化 1.1 哈希表的基本介绍 1.1.1 哈希表初始化接口示例 1.1.2 哈希表的键值的注意事项 1.1.3 自定义 ...

  4. c++ map 修改value_哈希表:其实需要哈希的地方都能找到map的身影

    给「代码随想录」一个星标吧! ❝ 数组和set都靠边站! ❞ 第454题.四数相加II 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A ...

  5. C++ STL : 模拟实现STL中的关联式容器unordered_map/unordered_set

    目录 unordered_map/unordered_set unordered_map/unordered_set与map/set的区别 底层哈希桶的改造 仿函数 Key值的获取方法 hash(ke ...

  6. c++的STL中的map(哈希表)与unordered_map

    map: unordered_map: map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的 unordered_map:unordered_map内部实 ...

  7. 【C++】STL —— unordered_map/unordered_set的基本使用

    目录 unordered系列关联式容器 一.unordered_set 1. unordered_set的介绍 2. unordered_set的使用 3. unordered_set/unorder ...

  8. 【C++】-- STL之unordered_map/unordered_set详解

    目录 一.map/set和unordered_map/unordered_set的区别 二.unordered_set 1.特点 2.构造 (1)构造一个空的 unordered_set对象 (2) ...

  9. C++ 使用哈希表封装模拟实现unordered_map unordered_set

    一.unordered_map unordered_set 和 map set的区别 1. map set底层采取的红黑树的结构,unordered_xxx 底层数据结构是哈希表.unordered_ ...

最新文章

  1. android小球移动代码,Android自定义圆形View实现小球跟随手指移动效果
  2. linux root用户无法ssh,root用户无法通过ssh连接Linux系统
  3. c++创建单级目录 多级目录,判断是否存在
  4. Spring Boot 2.X 使用@Cacheable时注意事项
  5. use SQVI to display table join
  6. 小红书成立六周年内部信:月活用户量已经突破8500万
  7. linux命令th,Linux 第13天 文本操作命令
  8. bzoj 1406: [AHOI2007]密码箱
  9. python运维常用脚本
  10. 爬取三个acm网站题库(neuqoj pku hdu)
  11. STM32涉及到的汇编基础知识
  12. 营收数据增长的京东物流,期待“外部探索”
  13. c语言程序中u8是什么意思,c – __u8和uint8_t之间的区别
  14. matlab里qmul,APE: Audio Perceptual Evaluation Toolbox for MATLAB
  15. nodejs基于微信小程序的图书销售商城系统 uniapp 小程序
  16. margin-left:-100%理解
  17. 中国信通院的星火链主链支持与以太链(测试网)交互
  18. 设顺序表va中的数据元素递增有序。试写一算法,将x插入到顺序表的适当位置上,以保持该表的有序性。
  19. 结合MBTI人格理论探讨爱因斯坦的两次婚姻
  20. 极市直播丨严彬-Unicorn:走向目标跟踪的大一统(ECCV2022 Oral)

热门文章

  1. java0到9的字符怎么表示_java,_java 怎么生成一个0-9,a-z的一个44位字符串作为上传文件的名字,java - phpStudy...
  2. bee 字符串转int_beego中gbk和utf8编码转换问题
  3. 信安精品课:2020年软考信息安全工程师备考公开课
  4. 宝塔面板的ftp无法使用解决
  5. Atcoder Grand Contest 026 (AGC026) F - Manju Game 博弈,动态规划
  6. 通常情况下的中国剩余定理
  7. 软件体系架构课下作业07
  8. Mysql事务探索及其在Django中的实践(二)
  9. Objective-C 类和对象
  10. 【原】母版页、皮肤、导航 那点事 Master Pages Themes and Navigation Controls FAQ