數據結構:哈希表


我們一直在講哈希,哈希,但是真正用這個數據結構的時候往往采用的是它的簡化形式

那么如何構造一個真正的哈希表呢?

首先我們明確一下哈希表是干啥用的,沒錯就是用來判重和查找的

但是這個判重,我們要規定一下限制范圍, 雖然哈希表功能強大但是還是有局限性

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

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

在這里最好不用調用什么函數,你不懂它

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數組的范圍

這里的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 } 

 


免責聲明!

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



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