C++哈希表的使用


1.C++ STL unordered_map用法

在C++11中,unordered_map作為一種關聯容器,替代了hash_map,unordered_map的底層實現是hash表,所以被稱為無序關聯容器。使用時需要指明頭文件 #include<map>。

不管是map還是unordered_map都是一種 key-map(value) 映射的容器,提供非常高的查找效率。

2.成員

2.1 類型定義

  • unordered_map::const_iterator    受控序列的常量迭代器。

Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

for (Mymap::const_iterator it = c1.begin(); it != c1.end(); ++it) 
        std::cout << " [" << it->first << ", " << it->second << "]";
output:

[a,1],[b,2],[c,3]
  • unordered_map :: const_local_iterator    受控序列的常量存儲桶迭代器。用法和const_iterator類似。

  • unordered_map :: const_pointer    指向元素的常量指針。

Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

for (Mymap::iterator it = c1.begin(); it != c1.end(); ++it) 
{ 
      Mymap::const_pointer p = &*it; 
      std::cout << " [" << p->first << ", " << p->second << "]"; 
}
output:
[a,1],[b,2],[c,3]
  • unordered_map :: const_reference    該類型描述了一個對象,該對象可用作對受控序列元素的恆定引用。

Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

for (Mymap::iterator it = c1.begin(); it != c1.end(); ++it) 
{ 
      Mymap::const_reference ref = *it; 
      std::cout << " [" << ref.first << ", " << ref.second << "]"; 
}
output:
[a,1],[b,2],[c,3]
  • unordered_map::hasher    哈希函數。

Mymap c1; 
Mymap::hasher hfn = c1.hash_function(); 
std::cout << "hfn('a') == " << hfn('a') << std::endl; 
std::cout << "hfn('b') == " << hfn('b') << std::endl; 
output:
hfn('a') == 3826002220
hfn('b') == 3876335077
  • unordered_map :: iterator    受控序列的迭代器類型。用法和const_iterator類似。

  • unordered_map :: key_equal    比較函數。

Mymap c1; 
 
Mymap::key_equal cmpfn = c1.key_eq(); 
std::cout << "cmpfn('a', 'a') == " << std::boolalpha << cmpfn('a', 'a') << std::endl; 
std::cout << "cmpfn('a', 'b') == " << std::boolalpha << cmpfn('a', 'b') << std::endl; 
output:
true, false

2.2 成員函數

  •  begin    指定受控序列或存儲桶的開始。
Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

for (Mymap::const_iterator it = c1.begin(); it != c1.end(); ++it) 
        std::cout << " [" << it->first << ", " << it->second << "]"; 
output:
[a,1],[b,2],[c,3]
  • bucket    獲取鍵值的存儲桶(存儲區)編號。
Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

Mymap::size_type bs = c1.bucket('a'); 
std::cout << "bucket('a') == " << bs << std::endl; 
  • clear    刪除所有元素。
Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

c1.clear(); 
std::cout << "size == " << c1.size() << std::endl; 
std::cout << "empty() == " << std::boolalpha << c1.empty() << std::endl; 
output:
size == 0 empty()== true
  • count    查找與指定鍵匹配的元素數。
Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

std::cout << "count('A') == " << c1.count('A') << std::endl; 
std::cout << "count('b') == " << c1.count('b') << std::endl; 
output:
count('A')== 0 count('b')== 1
  • empty    判斷是否為空。
  • erase    刪除指定位置的元素。兩種形式:

    iterator erase(iterator where); // 要移除的位置通過where來指定
    iterator erase(iterator first, iterator last); // 移除[first, last)范圍的元素
    
    Mymap c1; 
    c1.insert(Mymap::value_type('a', 1)); 
    c1.insert(Mymap::value_type('b', 2)); 
    c1.insert(Mymap::value_type('c', 3)); 
    
    Mymap::iterator it2 = c1.erase(c1.begin()); 
    std::cout << "*erase(begin()) == [" << it2->first << ", " << it2->second << "]"; 
    
    c1.insert(Mymap::value_type('d', 4)); 
    c1.insert(Mymap::value_type('e', 5)); 
    
    it2 = c1.end(); 
    it2 = c1.erase(c1.begin(), --it2); 
    std::cout << "*erase(begin(), end()-1) == [" << it2->first << ", " << it2->second << "]" << std::endl;
    [b,2]
    [e,5]
  • find    查找與指定鍵匹配的元素。
Mymap c1; 
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3)); 

std::cout << "find('A') == " << std::boolalpha << (c1.find('A') != c1.end()) << std::endl; 

std::cout << "find('b') == " << std::boolalpha << (it != c1.end()) << ": [" << it->first << ", " << it->second << "]" << std::endl; 
find('A')==false
find('b')== true
  • end    指定受控序列的結尾。注意:它所指的位置是迭代器最后一個元素位置的下一個位置。
Mymap c1;
c1.insert(Mymap::value_type('a', 1));
c1.insert(Mymap::value_type('b', 2));
c1.insert(Mymap::value_type('c', 3));

Mymap::iterator it2 = c1.end();
--it2;
std::cout << " [" << it2->first << ", " << it2->second << "]";
--it2;
std::cout << " [" << it2->first << ", " << it2->second << "]";
[c,3]
[b,2]
  • hash_function    獲取存儲的哈希函數對象。
  • insert    添加元素。
Mymap c1;  
c1.insert(Mymap::value_type('a', 1)); 
c1.insert(Mymap::value_type('b', 2)); 
c1.insert(Mymap::value_type('c', 3));
  • key_eq    判斷key是否相等。
  • swap    交換兩個容器的元素。
Mymap c1;
c1.insert(Mymap::value_type('a', 1));
c1.insert(Mymap::value_type('b', 2));
c1.insert(Mymap::value_type('c', 3));

for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it)
    std::cout << " [" << it->first << ", " << it->second << "]";
    std::cout << std::endl;

Mymap c2;
c2.insert(Mymap::value_type('d', 4));
c2.insert(Mymap::value_type('e', 5));
c2.insert(Mymap::value_type('f', 6));
c1.swap(c2);

for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it)
    std::cout << " [" << it->first << ", " << it->second << "]";
    std::cout << std::endl;

swap(c1, c2);

for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it)
    std::cout << " [" << it->first << ", " << it->second << "]";
    std::cout << std::endl;
[a,1],[b,2],[c,3]
[d,4],[e,5],[f,6]
[a,1],[b,2],[c,3]

 3. 哈希表的查找

理想的情況是希望不經過任何比較,一次存取便能得到所查記錄,那就必須在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系f,使每個關鍵字和結構中一個惟一的存儲位置相對應。因而在查找時,只要根據這個對應關系f找到給定值K的像f(K)。若結構中存在關鍵字和K相等的記錄,則必定在f(K)的存儲位置上,由此,不需要進行比較便可直接取得所查記錄。在此,我們稱這個對應關系f為哈希( Hash)函數,按這個思想建立的表為哈希表。

哈希函數的構造方法

哈希函數是從關鍵字集合到地址集合的映像。通常,關鍵字集合比較大,它的元素包括所有可能的關鍵字,而地址集合的元素僅為哈希表中的地址值。哈希函數其實是一個壓縮映像,那么這種情況就不可避免的產生沖突,那么在建造哈希表時不僅要設定一個好的哈希函數,還要設定一種處理沖突的方法。

  • 直接定址法

取關鍵字或關鍵字的某個線性函數值為哈希地址。即H(key)=key 或 H(key)=a*key+b   (a,b為常數)。

舉例:統計解放以后出生人口,其中年份作為關鍵字,哈希函數取關鍵字自身加一個常數H(key)=key+(-1948).查找1970年出生的人數,則直接查(1970-1948)=22項即可。

地址 01          02         03   ...           22          23            24  ...    

年份 1949     1950      1951   ...     1970

人數                     .............            15000

  • 除留余數法

取關鍵字被某個不大於哈希表表長m的數p除后所得余數為哈希地址(p為素數)

H(key)=key  MOD  p,p<=m   (最簡單,最常用)p的選取很重要

一般情況,p可以選取為質數或者不包含小於20的質因數的合數(合數指自然數中除了能被1和本身整除外,還能被其他數(0除外)整除的數)。

  • 數字分析法

  • 平方取中法

  • 折疊法

  • 隨機數法

常用沖突處理方式

  • 開放定址法

這個方法的基本思想是:當發生地址沖突時,按照某種方法繼續探測哈希表中的其他存儲單元,直到找到空位置為止。這個過程可用下式描述: 
H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1)) 
其中: H ( key ) 為關鍵字 key 的直接哈希地址, m 為哈希表的長度, di 為每次再探測時的地址增量。 
采用這種方法時,首先計算出元素的直接哈希地址 H ( key ) ,如果該存儲單元已被其他元素占用,則繼續查看地址為 H ( key ) + di 的存儲單元,如此重復直至找到某個存儲單元為空時,將關鍵字為 key 的數據元素存放到該單元。 
增量 d 可以有不同的取法,並根據其取法有不同的稱呼: 

( 1 ) d i = 1 , 2 , 3 , …… 線性探測再散列; 
( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2…… 二次探測再散列; 
( 3 ) d i = 偽隨機序列 偽隨機再散列;

  • 再散列函數法:(再哈希法)遇到沖突就重新采用一個散列函數計算新的存儲位置,可以使關鍵字不產生聚集

  • 鏈地址法(拉鏈)將所有關鍵字的同義詞記錄在一個單鏈表中,在散列表中只存儲所有同義詞表的頭指針

  • 建立一個公共溢出區法: 為所有沖突的關鍵字開辟一個公共的溢出區(表)來存放

4. 代碼

哈希表采用數組存儲,哈希函數構造和處理沖突的方法是除留余數法+開放定址法

/****
* Hash Table
*
****/


//#include "Global.h"
#include <iostream>
using namespace std;

// HashTable Data Structure Definition
// array hashtable
#define tablesize 10
typedef int HashTable[tablesize];
//hash function initialization way
void Initial_HashTable(HashTable &ht)
{
    for (int i = 0; i < tablesize; i++)
        ht[i] = 0;
}
//search hashtable function
int Search_HashTable(HashTable &ht,int key)
{
    int address = key%tablesize;
    int compare = 0;
    while (compare < tablesize&&ht[address] != key&&ht[address] != 0)
    {
        compare++;
        address = (address+1)%tablesize;
    }
    if (compare == 10 || ht[address] == 0)
        cout << "can not find elem" << endl;
    return address;
}
//insert hashtable function
void Insert_HashTable(HashTable &ht,int key) 
{
    int res = Search_HashTable(ht,key);
    if (ht[res] == 0) ht[res] = key;
}
//test function
int main()
{
    int data[8] = { 25,36,39,47,20,58,16,35 };
    HashTable ht;

    //initialization.
    Initial_HashTable(ht);

    //insert datas.
    for (int i = 0; i < 8; i++)
    {
        Insert_HashTable(ht, data[i]);
    }
    cout << endl;

    //search.
    cout << "25 : " << Search_HashTable(ht, 25) << endl;
    cout << "35 : " << Search_HashTable(ht, 35) << endl;
    cout << "145 : " << Search_HashTable(ht, 145) << endl;
    system("pause");
    return 0;
}

 

 5. 參考

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/bb982522(v=vs.90)

https://www.cnblogs.com/mydomain/p/11205623.html

https://www.cnblogs.com/ajianbeyourself/p/4358672.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM