哈希表是什么
怎么创建哈希表
哈希表是什么
哈希表又称散列表,是一种数据结构
本质就是一个数组。
怎么创建哈希表
实际上并没有定义哈希固有的一种数据结构,而是利用其他的数据结构,比如数组,再加上一些条件就形成了哈希这种数据结构。
1,利用数组创建哈希表
就是创建一个数组。
哈希表的数据插入
将存储的数据作为参数传递给哈希函数,哈希函数返回元素在哈希表中存储的下标。然后利用这个小标,将元素存储进入哈希表(实际上应该称为哈希数组)。
哈希查询
传递哈希数组和需要查询的元素。用过哈希函数返回元素在哈希数组中的下标,利用这个下标在哈希数组中直接查询出这个元素。
哈希数组和普通数组的区别
就多了一个用来查询下标的哈希函数而已。
哈希函数:
计算元素的存储位置,把输入的元素存储进入指定位置,返回这个位置的索引(下标)
哈希冲突/哈希碰撞
哈希函数通过计算,把多个值存储进入一个位置里,导致一个索引代表的位置存储多个数据。
hashtable
set 是自动排序的,而 hashset 则是无序的。除此之外,hashset 与 set 的对外接口完全相同(函数完全相同)。
因为hashtable是无序的,所以hashset和hashmap以hashtable为底层,其存储的元素也是无序的。
hashtbale的容量可以动态拓展吗
加有hash几个字的一些结构,底层都是由hashtable来提供的,不加的都是由红黑树来提供
hashset和hashmap
set和map是以红黑树作为底层数据结构。
hashset和hashmap是以hashtable作为底层数据结构。
hashset和hashmap的键值对
hashset<int> hset; //一个模板参数
hash_map<int,string> hmap; //两个模板参数
同样,和multiset和multimap一样,也有hash_multi_set和hashs_multi_map.
哈希可以存储的数据类型
哈希的实现
需要实现的部分有;
1,哈希函数
2,解决哈希冲突
1》链地址法:为每个哈希值维护一个链表,并将具有相同哈希值的元素都放入这一链表当中。
2》开放地址法:当发现哈希值 hh 处产生冲突时,根据某种策略,从 hh 出发找到下一个不冲突的位置。例如,一种最简单的策略是,不断地检查 h+1,h+2,h+3,\ldotsh+1,h+2,h+3,… 这些整数对应的位置。
3》再哈希法:当发现哈希冲突后,使用另一个哈希函数产生一个新的地址。
3,拓展
hashset的实现
vector<>就可以实现,因为只需要保存一个值。
方法一:链地址法(链表法)
class MyHashSet {
private:
vector<list<int>> data;
static const int base = 769;
static int hash(int key) {
return key % base;
}
public:
/** Initialize your data structure here. */
MyHashSet(): data(base) {}
void add(int key) {
int h = hash(key);
for (auto it = data[h].begin(); it != data[h].end(); it++) {
if ((*it) == key) {
return;
}
}
data[h].push_back(key);
}
void remove(int key) {
int h = hash(key);
for (auto it = data[h].begin(); it != data[h].end(); it++) {
if ((*it) == key) {
data[h].erase(it);
return;
}
}
}
/** Returns true if this set contains the specified element */
bool contains(int key) {
int h = hash(key);
for (auto it = data[h].begin(); it != data[h].end(); it++) {
if ((*it) == key) {
return true;
}
}
return false;
}
};
解析:
MyHashSet(): data(base) {}
=MyHashSet(): data(base,0) {}
开辟base个list<int>的链表,并初始化为0
vector<list<int>> data;
data[h].push_back(key);
这两句是不是冲突
data中应该存储的是一个个的链表,而不是一个int整数。
注意:不冲突,data[h]就是去出list<int>这个链表了,就是将key这个数据存入list<int>中了
void add(int key) {
int h = hash(key);//加入值之前,先获取值的键
for (auto it = data[h].begin(); it != data[h].end(); it++) {//获取键之后进行冲突的判断
if ((*it) == key) {
return;//如果是同一个值,直接返回
}
}
data[h].push_back(key);//如果此键可用,不管冲突不冲突,则将数据存入数组容器的链表中
2,数组法
#define MAX_LEN 100000
class MyHashSet {
private:
vector<vector<int>> set[MAX_LEN];
/* 返回相应的表索引 */
int getIndex(int key) {
return key % MAX_LEN;
}
/* 搜索特定存储哈希表中的值。如果值不存在,则返回-1 */
int getPos(int key, int index) {
for (int i = 0; i < set[index].size(); ++i) {
if (set[index][i] == key) {
return i;
}
}
return -1;
}
public:
MyHashSet() {
}
/* 向哈希集合中插入值key */
void add(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
/* 如果不存在则添加新的数据进去 存在不做任何操作 */
if (pos < 0) {
set[index].push_back(key);
}
}
/* 将给定值key从哈希集合中删除 */
void remove(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
if (pos >= 0) {
set[index].erase(set[index].begin() + pos);
}
}
/** 返回哈希集合中是否存在这个值 key */
bool contains(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
return pos >= 0;
}
};
和链表法的不同之处:
1,判断同一个值是否已经存在用函数实现(因为是每个函数的公共部分)。
相同点:
1,将具有相同哈希值的数据存储进入一个内部容器。(冲突的元素)
哈希实现特点:
1,无论进行任何操作,都先获取哈希值(键值)。
2,哈希函数设置选择的数组要选择质数(大于1的自然数中,除了1和它本身没有其他因素)。
用一个质数当作基数时可以使得哈希表中每个位置都尽可能的被使用到。
可以减少冲突。
hashmap的实现
问题:有键值对,怎么实现
保存键值对的一定是pair模板结构体。
所以容器存储的是pair对象,或者pair临时对象。
可以创建pair结构体对象,再加入容器
也可以直接用:make_pair(key,value)作为参数(pair临时值),直接加入容器。
make_的临时值
哈希的冲突解决
再开建地---链接