散列表處理沖突的方法


 選擇一個“好”的散列函數可以在在一定程度上減少沖突,但在實際應用中很難完全避免沖突,所以選擇一個有效的處理沖突的方法是非常重要的。創建散列表和查找散列表都會遇到沖突,這兩種情況下處理沖突的方法應該是一致的。

創建散列表的方法和散列表本身的組織形式有關。按照組織形式的不同通常分位兩大類:開放地址法和鏈地址法。

1.開放地址法

開放地址法的基本思想時:將記錄都存儲在散列表數組中,當某一記錄關鍵字經過散列函數的計算所得到的初始地址H0此時已有其他關鍵字占據(即發生沖突),采取合適的方法計算得到另一個地址,如果仍然發生沖突,就以此地址為基礎采取相同的計算方法,知道所得到的地址不再發生沖突。

這種方法在尋找下一個地址時,原來的數組空間對於所有的元素都是開放的,不管關鍵字key經過哈希函數計算所得到的初始地址是否與之相同,也沒有其他的要求任何的元素中可以存儲,所以稱作開放地址法。

可用公式表示為

Hi=(H(key)+di)%m​   

其中H(key)表示哈希函數,m為散列表長度,di為增量序列。

根據di取值的不同可以分為以下兩種情況

(1)線性探測法

di= 1,2,3,...,m-1

這種探測方法可以將散列表假象成一個循環表(實際並不是),發生沖突時,從沖突的下一單元順序尋找空單元,如果最后一個位置都沒有找到空單元,則回到表頭繼續查找,直到找到一個空位,就把此單元放入此空位。如果直到發生沖突的第一個單元(即經過哈希函數所得到的第一個初始地址)都沒空位,則說明散列表已滿,需要進行溢出處理

 

將一組關鍵字(9,1,23,14,55,20,84,27),采用散列函數:H(key)=key%7,表長為10,用線性探測法處理沖突

9%7=2    1%7=1    23%7=2(與9發生沖突)H=(H0+di)%(10-1)    di=1      14%7=0

55%7=6     20%7=6(與55發生沖突)H=(H0+di)%(10-1)  di=1

84%7=0(與14發生沖突) di=1,與1發生沖突 di=2,與9發生沖突 di=3,與23發生沖突 di=4,無沖突,所以di=4

27%7=6,與55,20發生沖突,所以di=2

(2)二次探測法

di=1^{2},-1^{2},2^{2},-2^{2},...+k^{2},-k^{2}​      k<=m/2(-1,-2的意思是可以向反方向探測)先右后左

將一組關鍵字(9,1,23,14,55,20,84,27),采用散列函數:H(key)=key%7,表長為10,用二次探測法處理沖突

 注意不是每次加di,而是從每加一次di都是從初始位置開始加的

2.鏈地址法

鏈地址法的基本思想是:將具有相同散列地址的記錄放在同一個單鏈表中,稱為同義詞鏈表,不需要處理沖突。

以前面相同的題為例,采用尾插法,下面是示意圖

 

 

以下是代碼演示

線性探測法的代碼實現

//開放地址法散列表的創建(從0開始)
void CreateHashTable(HashTable a[])
{
    int n;//存儲數據個數
    cout<<"請輸入數據的個數"<<endl;
    cin>>n;
    int c;//將地址存放於次
    int i;
    int key;//存放key
    int j;
    int h=0;//總的查找次數
    for(i=0;i<m;i++)//0-19存放數據
    {
        a[i].key=0;//賦初值
    }
    for(i=0;i<n;i++)
    {
        cout<<"請輸入數據"<<endl;
        cin>>key;
        c=key%16;//哈希函數為key%16
        h++;
        if(a[c].key==0) {a[c].key=key;}//一次存儲成功
        else
            for(j=c+1;;j++)//di可以改成不同的數
            {
                h++;
                if(a[j%m].key==0)
                    {a[j%m].key=key;
                    break;}
                if(j%m==c)
                {
                    cout<<"存儲數據個數超過哈希表存儲能力"<<endl;
                    break;
                }
            }

    }
    printf("ASL為%d/%d\n",h,n);

}

 鏈式探測法的代碼實現

 1 void CreateHashTableNode(LNode* a[])
 2 {
 3     int n;
 4     int i;
 5     int c;
 6     LNode *p;
 7     cout<<"請輸入關鍵數個數"<<endl;
 8     cin>>n;
 9    // LNode* a[m];
10     for(i=0;i<m;i++)//初始化
11     {
12         a[i]=new LNode;
13         a[i]->next=NULL;
14     }
15     int key;//存儲key
16     cout<<"請輸入關鍵字"<<endl;
17     for(i=0;i<n;i++)//n個關鍵字的存儲
18     {
19         cin>>key;
20         c=key%16;//哈希函數
21         p=new LNode;
22         p->data=key;
23         p->next=a[c]->next;
24         a[c]->next=p;
25     }
26 
27 
28 }

 



免責聲明!

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



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