一、哈希表
1.使用哈希的前提
在實際編程中,我們常常面臨着兩個問題:存儲和查詢,這兩個過程的效率往往制約着整個程序的效率,而我們常見的存儲數據的數據結構比如線性表,樹,圖等,數據在結構中的位置往往是不明確的,當我們在這些數據結構中要查詢一個數據,都避免不了去執行查詢算法,去遍歷數據結構,拿關鍵字和結構中的數據進行一一比較,從而得到想要的數據,我們就希望能不能不通過比較就能獲得我們想要的結果呢?
答案是有的,不通過任何比較,一次存儲便能取得所查記錄,但這就必須在記錄的存儲位置和他的關鍵字之間建立一個確定的對應關系f,使得每個關鍵字和結構中的唯一的存儲位置相對應,這個關系就是我們所說的哈希函數f(x),在這個思想上建立起來的表就成為哈希表。
2.介紹
哈希表是一種使用哈希函數組織數據,以支持快速插入和搜索的數據結構。
有兩種不同類型的哈希表:哈希集合和哈希映射。
哈希集合是集合數據結構的實現之一,用於存儲非重復值。
哈希映射是映射數據結構的實現之一,用於存儲(key, value)鍵值對。
在標准模板庫的幫助下,哈希表是易於使用的。大多數常見語言(如Java,C ++ 和 Python)都支持哈希集合和哈希映射。
通過選擇合適的哈希函數,哈希表可以在插入和搜索方面實現出色的性能。
原理
哈希表的關鍵思想是使用哈希函數將鍵映射到存儲桶。更確切地說,
當我們插入一個新的鍵時,哈希函數將決定該鍵應該分配到哪個桶中,並將該鍵存儲在相應的桶中;
當我們想要搜索一個鍵時,哈希表將使用相同的哈希函數來查找對應的桶,並只在特定的桶中進行搜索。
在示例中,我們使用 y = x % 5 作為哈希函數。讓我們使用這個例子來完成插入和搜索策略:
插入:我們通過哈希函數解析鍵,將它們映射到相應的桶中。
例如,1987 分配給桶 2,而 24 分配給桶 4。
搜索:我們通過相同的哈希函數解析鍵,並僅在特定存儲桶中搜索。
如果我們搜索 1987,我們將使用相同的哈希函數將1987 映射到 2。因此我們在桶 2 中搜索,我們在那個桶中成功找到了 1987。
例如,如果我們搜索 23,將映射 23 到 3,並在桶 3 中搜索。我們發現 23 不在桶 3 中,這意味着 23 不在哈希表中。
3.STL的使用
#include <unordered_set> // 當使用set時引用的模板庫
int main() {
// 創建一個哈希集合
unordered_set<int> hashset;
// 插入新的關鍵字
hashset.insert(3);
hashset.insert(2);
hashset.insert(1);
// 刪除關鍵字
hashset.erase(2);
// 判斷關鍵字是否在哈希集合中,如果在方法返回負數。如果在集合中返回真,否則返回假
if (hashset.count(2) <= 0) {
cout << "關鍵字2不在哈希集合中" << endl;
}
// 得到哈希集合的大小
cout << "The size of hash set is: " << hashset.size() << endl;
// 遍歷哈希集合,注意要以指針的形式
for (auto it = hashset.begin(); it != hashset.end(); ++it) {
cout << (*it) << " ";
}
cout << "are in the hash set." << endl;
// 清除哈希集合
hashset.clear();
// 判斷哈希結合是否為空
if (hashset.empty()) {
cout << "hash set is empty now!" << endl;
}
}
二、unordered_map和unordered_set
(一)、實現原理
unordered_map內部實現了一個哈希表,也叫散列表,通過把關鍵碼值映射到Hash表中一個位置來訪問記錄,查找的時間復雜度可達到O(1),其在海量數據處理中有着廣泛應用。因此,其元素<key,value>的排列順序是無序的。
unordered_set底層也是哈希表,只是存儲的是value,而不是<key,value>
c++中對unordered_set描述大體如下:無序集合容器(unordered_set)是一個存儲唯一(unique,即無重復)的關聯容器(Associative container),容器中的元素無特別的秩序關系,該容器允許基於值的快速元素檢索,同時也支持正向迭代。
在一個unordered_set內部,元素不會按任何順序排序,而是通過元素值的hash值將元素分組放置到各個槽(Bucker,也可以譯為“桶”),這樣就能通過元素值快速訪問各個對應的元素(均攤耗時為O(1))。
就是說我們只需要通過某個函數將關鍵字和其存儲位置相對應起來即可:
存儲位置 = f(關鍵字)
我們通過查找關鍵字不需要比較就可以獲得需要記錄的存儲位置,這是一種新的存儲技術----散列存儲技術(將記錄存儲到其關鍵字對應的f(key)內存位置)
這種對應關系f稱為哈希函數(Hash),然后以這種散列存儲方式將記錄存儲到一塊連續的存儲空間中,這塊連續存儲空間稱為散列表或者哈希表(Hash Table).
(二)、內部公共接口
1.查找
1.count(key):通過key查找,找到返回1,找不到返回0。
2.find(key):返回key對應的迭代器,如果key不存在,則find返回unordered_map::end因此可以通過判斷map.find(key) == map.end()來判斷,key是否存在於當前的unordered_map中,
2.迭代器--iterator
unordered_map
/*
c++ 里面的map容器的迭代器里面 有個first 和 second
例如
map<string, int> m; <key,value>
m["one"] = 1;
map<string, int>::iterator p = m.begin();
p->first; // 這個是 string 值是 "one"
p->second; //這個是 int 值是 1
*/
unordered_set
/*
函數聲明 功能介紹
begin 返回unordered_set第一個元素的迭代器
end 返回unordered_set最后一個元素下一個位置的迭代器
cbegin 返回unordered_set第一個元素的const迭代器
cend 返回unordered_set最后一個元素下一個位置的const迭代器
*/
3.其他函數
unordered_set
/*
函數聲明 功能介紹
bool empty() const 檢測unordered_set是否為空
size_t size() const 獲取unordered_set的有效元素個數
iterator find(const key_type& k) 返回k在哈希桶中的位置
size_t count(const key_type& k) 返回哈希桶中關鍵碼為k的鍵值對的個數
insert 向容器中插入鍵值對
erase 刪除容器中的鍵值對
void clear() 清空容器中有效元素個數
void swap(unordered_set&) 交換兩個容器中的元素
size_t bucket_count()const 返回哈希桶中桶的總個數
size_t bucket_size(size_t n)const 返回n號桶中有效元素的總個數
size_t bucket(const key_type& k) 返回元素key所在的桶號
*/
4.unordered_set的構造函數
unordered_set<int> set1; //創建空set
unordered_set<int> set2(set1); //拷貝構造,將set1的數據賦值給set2
unordered_set<int> set3(set1.begin(), set1.end()); //迭代器構造,如果有重復的,自動刪除
unordered_set<int> set4(arr,arr+5); //數組構造
unordered_set<int> set5(move(set2)); //移動構造
unordered_set<int> set6 {1,2,10,10};//使用initializer_list初始化
5.常用函數
set1.find(2); //查找2,找到返回迭代器,失敗返回end()
set1.count(2); //返回指2出現的次數,0或1
set1.emplace(3); //使用轉換移動構造函數,返回pair<unordered_set<int>::iterator, bool>
set1.insert(3); //插入元素,返回pair<unordered_set<int>::iterator, bool>
set1.insert({1,2,3}); //使用initializer_list插入元素
set1.insert(set1.end(), 4);//指定插入位置,如果位置正確會減少插入時間,返回指向插入元素的迭代器
set1.insert(set2.begin(), set2.end());//使用范圍迭代器插入
set1.erase(1); //刪除操作,成功返回1,失敗返回0
set1.erase(set1.find(1)); //刪除操作,成功返回下一個pair的迭代器
set1.erase(set1.begin(), set1.end()); //刪除set1的所有元素,返回指向end的迭代器
set1.empty(); //是否為空
set1.size(); //大小
set1.bucket_count(); //返回容器中的桶數
set1.bucket_size(1); //返回1號桶中的元素數
set1.bucket(1); //1在哪一個桶
set1.load_factor(); //負載因子,返回每個桶元素的平均數,即size/float(bucket_count);
set1.max_load_factor();//返回最大負載因子
set1.max_load_factor(2);//設置最大負載因子為2,rehash(0)表示強制rehash
set1.rehash(20);//設置桶的數量為20,並且重新rehash
set1.reserve(20);//將容器中的桶數設置為最適合元素個數,如果20大於當前的bucket_count乘max_load_factor,則增加容器的bucket_count並強制重新哈希。如果20小於該值,則該功能可能無效。
unordered_set<int>::iterator it = set1.begin(); //返回指向set1首元素的迭代器
unordered_set<int>::const_iterator c_it = set1.cbegin(); //返回指向set1首元素的常量迭代器
unordered_set<int>::local_iterator it = set1.begin(1);//返回1號桶中的首元素迭代器
unordered_set<int>::const_local_iterator c_it = set1.cbegin(1);//返回1號桶中的首元素的常量迭代器
pair<unordered_set<int>::iterator, unordered_set<int>::iterator> it = set1.equal_range(1);//返回一個pair,pair里面第一個變量是lower_bound返回的迭代器,第二個迭代器是upper_bound返回的迭代器
set1.clear(); //清空