哈希表的構造方法、沖突處理方法及哈希拉鏈法的簡單代碼實現


  由於哈希表的查找高效性,在平時的算法中用的也是比較多。例如:字符串、單詞個數的統計,只出現一次字符或者數字的統計,兩個集合相同元素的查找等等,還有插入刪除的高效(鏈地址法)都可以用哈希表來解決。所以這里對其做一個小小的總結。缺點可能是需要占用額外的內存空間。

一、哈希函數的構造方法
下面介紹五種常用的哈希構造方法:
構造哈希函數的原則是:
(1)函數本身便於計算;
(2)計算出來的地址分布均勻,即對任一關鍵字k,f(k) 對應不同地址的概率相等,目的是盡可能減少沖突。
1、除留余數法;
 取關鍵字被某個不大於哈希表長m的數p除后所得的余數為哈希地址。即:
          H(key)=key MODE p,p<=m.(p的取值最好為素數)。
 若沖突較多,可取較大的m和p值。
2、隨機法;
采用一個偽隨機函數做哈希函數,即:
         H(key)=random(key)。其中random為隨機函數。
通常,當關鍵字長度不等時采用此法構造哈希函數較為恰當。
3、平方取中法;
當無法確定關鍵字中哪幾位分布較均勻時,可以先求出關鍵字的平方值,然后按需要取平方值的中間幾位作為哈希地址。
這是因為:平方后中間幾位和關鍵字中每一位都相關,故不同關鍵字會以較高的概率產生不同的哈希地址。
例如對於關鍵key:123。1234^2=1522756,H(k)關鍵字的哈希地址為:227.
4、折疊法;
      這種方法是按哈希表地址位數將關鍵字分成位數相等的幾部分(最后一部分可以較短),然后將這幾部分相加,舍棄最高進位后的結果就是該關鍵字的哈希地址。具體方法有折疊法與移位法。移位法是將分割后的每部分低位對齊相加,折疊法是從一端向另一端沿分割界來回折疊(奇數段為正序,偶數段為倒序),然后將各段相加。
例如:key=12360324711202065,哈希表長度為1000,則應把關鍵字分成3位一段,在此舍去最低的兩位65,分別進行移位疊加和折疊疊加,求得哈希地址為105和907。

5、直接定址法;
取關鍵字或關鍵字的某個線性函數值為哈希地址。即:
        H(key)=key  或 H(key)=a*key+b
其中a、b為常數(這種hash函數叫做自身函數)。
6、數字分析法;
  如果事先知道關鍵字集合,並且每個關鍵字的位數比哈希表的地址碼位數多時,可以從關鍵字中選出分布較均勻的若干位,構成哈希地址。
例如,有1000個記錄,關鍵字為10位十進制整數d1d2d3…d7d8d9d10,如哈希表長取1200,則哈希表的地址空間為:000~1199。假設經過分析,各關鍵字中 d3、d5和d7的取值分布較均勻,則哈希函數為:h(key)=h(d1d2d3…d7d8d9d10)=d3d5d7。
例如,h(3748597089)=457,h(9846372561)=432。就是找數字中分布均勻的數字。
二、處理沖突的方法:
1、開放定址法,又稱下標加1法

這種方法也稱再散列法,其基本思想是:當關鍵字key的哈希地址p=H(key)出現沖突時,以p為基礎,產生另一個哈希地址p1,如果p1仍然沖突,再以p為基礎,產生另一個哈希地址p2,…,直到找出一個不沖突的哈希地址pi ,將相應元素存入其中。這種方法有一個通用的再散列函數形式:
    Hi=(H(key)+di)% m   i=1,2,…,n
    其中H(key)為哈希函數,m 為表長,di稱為增量序列。增量序列的取值方式不同,相應的再散列方式也不同。主要有以下三種:
  (1)線性探測再散列
  (2)二次探測再散列
  (3)偽隨機探測再散列
    缺點是:線性探測再散列容易產生“二次聚集”。當刪除某個數據的時候,需要設置標記或者移動數據,否則會導致查找的中斷。
2、再哈希法:
這種方法是同時構造多個不同的哈希函數:
    Hi=RH1(key)  i=1,2,…,k
當哈希地址Hi=RH1(key)發生沖突時,再計算Hi=RH2(key)……,直到沖突不再產生。這種方法不易產生聚集,但增加了計算時間。
3、鏈地址法;需要額外的空間;
   這種方法的基本思想是將所有哈希地址為i的元素構成一個稱為同義詞鏈的單鏈表,並將單鏈表的頭指針存在哈希表的第i個單元中,因而查找、插入和刪除主要在同義詞鏈中進行。鏈地址法適用於經常進行插入和刪除的情況。
4、公共溢出區;
這種方法的基本思想是:將哈希表分為基本表和溢出表兩部分,凡是和基本表發生沖突的元素,一律填入溢出表
三、哈希表拉鏈法的具體實現
 鏈地址法(拉鏈法)
當存儲結構是鏈表時,多采用拉鏈法,用拉鏈法處理沖突的辦法是:把具有相同散列地址的關鍵字(同義詞)值放在同一個單鏈表中,稱為同義詞鏈表。有m個散列地址就有m個鏈表,同時用指針數組T[0..m-1]存放各個鏈表的頭指針,凡是散列地址為i的記錄都以結點方式插入到以T[i]為指針的單鏈表中。T中各分量的初值應為空指針。
哈希表拉鏈法查找的具體實現代碼:

#include <iostream>
using namespace std;
#define  MODLE 13
struct Haxi_Table
{
    int data;//記錄一共有多少數據
    char a;
    Haxi_Table *next;
};

Haxi_Table *haxi_table[MODLE];//哈希表數組;
void Create_Haxi(int arry[],int num)
{
    for(int i=0;i<num;i++)
    {
        int index=arry[i]%MODLE;
        Haxi_Table *temp=new Haxi_Table;
        temp->a=i+97;
        temp->data=num;
        temp->next=NULL; 
        if(!haxi_table[index])
        {
            haxi_table[index]=temp;
        }
        else
        {
            temp->next=haxi_table[index];
            haxi_table[index]=temp;
        }
    }
}
char FindValue(int value)
{
    int index=value%MODLE;
    Haxi_Table *p=haxi_table[index];
    while(p)
    {
        if(p->data=value)
        {
            return p->a;
        }
        else
        {
            p=p->next;
        }
    }
    return -1;
}
void DestoryHash()
{
    Haxi_Table *temp=NULL;
    for(int i=0;i<MODLE;i++)
    {
        if(haxi_table[i])
        {
            while(haxi_table[i])
            {
                temp=haxi_table[i];
                haxi_table[i]=haxi_table[i]->next;
                delete temp;
            }
        }
    }
}
int main()
{
    int num;
    cout<<"please input the number of your data:"<<endl;
    cin>>num;
    int *array=new int[num];
    cout<<"please input the "<<num<<" data:"<<endl;
    for(int i=0;i<num;i++)
        cin>>array[i];
    Create_Haxi(array,num);
    cout<<"查找結果,8對應的字符為:"<<FindValue(8)<<endl;
    DestoryHash();
    system("pause");
    return 0;
}

 


免責聲明!

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



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