數據結構:哈希表

咱們一直在講哈希,哈希,可是真正用這個數據結構的時候每每採用的是它的簡化形式ios

那麼如何構造一個真正的哈希表呢?數組

首先咱們明確一下哈希表是幹啥用的,沒錯就是用來判重和查找的數據結構

可是這個判重,咱們要規定一下限制範圍, 雖然哈希表功能強大可是仍是有侷限性函數

哈希表適合那種數據特別多,可是對於每個數據,它的值不是很大的狀況spa

在使用哈希表以前,咱們先來進行一個科普,C++的int轉string與string轉intcode

在這裏最好不用調用什麼函數,你不懂它blog

string轉int 
istringstream is("12"); //構造輸入字符串流,流的內容初始化爲「12」的字符串 
int i; 
is >> i; //從is流中讀入一個int整數存入i中
int轉string
ostringstream os; //構造一個輸出字符串流,流內容爲空 
int i = 12; 
os << i; //向輸出字符串流中輸出int整數i的內容 
cout << os.str() << endl; //利用字符串流的str函數獲取流中的內容 

首先咱們給出哈希表的一些定義內存

const int max_hash_size=1000005;
const int maxn=1005;
int head[max_hash_size];  //存數據的數組 
int Next[maxn];  //記錄同地址元素的下一個元素的值
int st[maxn];  //數據集 

爲啥這個max_hash_size很大,由於這個範圍是每個元素最大是多少而不是一共有多少個元素,哈希表就是用來解決元素很是多的那種狀況的字符串

若是單純使用bool數組的話受制於內存的限制,查找能力實在有限,哈希表在本質上就是拓寬了這個bool數組的範圍string

這裏的head和Next就是鏈表,而後咱們給出哈希函數

int Hash(int& s)
{
    return(s*2654435769)>>28;
}
int HashingDouble(double d)
{
    if (d==0)
    return 0;
    else
    {
        int exponent;
        double mantissa = frexp(d, &exponent);
        return (unsigned int)((2*mantissa-1) * (~0U));
    }
}
int HashingString(char *str, int iLen)
{
    int hsval = 2654435769;
    int i;
    int iShift = 0;
    for(i=0; i<iLen; i++)
    {
        hsval ^= (str[i]<<iShift);
        iShift+=3;
        if(iShift>24)
            iShift=0;
    }
    return hsval;
}

你能夠把哈希函數理解爲根據元素的值來計算這個元素下標的函數,而後咱們回頭去看那個max_hash_size,而後理解一下:

若是咱們不用哈希函數,那麼數據有多少,max_hash_size就是多少,若是咱們用了哈希函數,就把原數據範圍進行了一個放縮,放縮到了空間所容許的一個範圍內

這個時候咱們再取理解maxn是啥,既然max_hash_size是通過放縮以後的數據有多少個,那麼maxn就是每個數據的數據範圍了

因爲哈希碰撞的緣由,同哈希值元素可能有多個,咱們在鏈表查找到地址的時候,要順着鏈表一個一個找看是否是

下面給出嘗試插入哈希表的函數,若是不重複就能夠插進去,不然就插不進去返回0

int try_to_insert(int s)
{
    int h=Hash(st[s]);  //獲得s元素的數組下標 
    int u=head[h];  //獲得這個下標下的元素值 
    while(u)  //順着鏈表一個一個找 
    {
        if(st[u]==st[s])  //若是在鏈表中找到了s,說明這個s已經訪問過了 
            return 0;   
        u=Next[u];  //此時u是下一個在此位置的元素值 
    }
    Next[s]=head[h];  //頭插法 
    head[h]=s;
    return 1; 
}

由於實現的過程當中有一個

int u=head[h];

while(u)

這麼一個操做,這應該是這種方法實現哈希表的一個缺陷,因此在插入的時候,千萬保證非0

下面給出哈希表完整的實現代碼,若是有更好的實現方法,會在本篇文章的基礎上迭代

 1 //適用於數據特別多可是每一個數的範圍不是很大的狀況下查找 
 2 /*
 3 string轉int 
 4 istringstream is("12"); //構造輸入字符串流,流的內容初始化爲「12」的字符串 
 5 int i; 
 6 is >> i; //從is流中讀入一個int整數存入i中
 7 int轉string
 8 ostringstream os; //構造一個輸出字符串流,流內容爲空 
 9 int i = 12; 
10 os << i; //向輸出字符串流中輸出int整數i的內容 
11 cout << os.str() << endl; //利用字符串流的str函數獲取流中的內容 
12 */ 
13 #include<iostream>
14 #include<cstring>
15 #include<cmath>
16 using namespace std;
17 const int max_hash_size=1000005;
18 const int maxn=1005;
19 int head[max_hash_size];  //存數據的數組 
20 int Next[maxn];  //記錄同地址元素的下一個元素的值
21 int st[maxn];  //數據集 
22 void init_lookup_table()
23 {
24     memset(head,0,sizeof(head));
25 } 
26 int Hash(int& s)
27 {
28     return(s*2654435769)>>28;
29 }
30 int HashingDouble(double d)
31 {
32     if (d==0)
33     return 0;
34     else
35     {
36         int exponent;
37         double mantissa = frexp(d, &exponent);
38         return (unsigned int)((2*mantissa-1) * (~0U));
39     }
40 }
41 int HashingString(char *str, int iLen)
42 {
43     int hsval = 2654435769;
44     int i;
45     int iShift = 0;
46     for(i=0; i<iLen; i++)
47     {
48         hsval ^= (str[i]<<iShift);
49         iShift+=3;
50         if(iShift>24)
51             iShift=0;
52     }
53     return hsval;
54 }
55 int try_to_insert(int s)
56 {
57     int h=Hash(st[s]);  //獲得s元素的數組下標 
58     int u=head[h];  //獲得這個下標下的元素值 
59     while(u)  //順着鏈表一個一個找 
60     {
61         if(st[u]==st[s])  //若是在鏈表中找到了s,說明這個s已經訪問過了 
62             return 0;   
63         u=Next[u];  //此時u是下一個在此位置的元素值 
64     }
65     Next[s]=head[h];  //頭插法 
66     head[h]=s;
67     return 1; 
68 }
69 int main()
70 {
71     //必須保證數據集中,查找的位置有值 
72     for(int i=1;i<=10;i++)
73         st[i]=i-1;
74     init_lookup_table();
75     for(int i=1;i<=5;i++)
76     {
77         if(try_to_insert(i))
78             cout<<"Yes"<<endl;
79         else
80             cout<<"No"<<endl;
81     }
82     cout<<endl;
83     for(int i=3;i<=7;i++)
84     {
85         if(try_to_insert(i))
86             cout<<"Yes"<<endl;
87         else
88             cout<<"No"<<endl;
89     }
90     return 0;
91 } 
相關文章
相關標籤/搜索