DataStructure-8.3-散列表的查找技术

来源:互联网 发布:linux spi驱动移植 编辑:程序博客网 时间:2024/06/05 18:55

8.3 散列表的查找技术

8.3.1散列表(哈希表):

   采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表,将关键码映射为散列表中适当存储位置的函数称为散列函数,所得的存储位置称为散列地址.


8.3.2 具体的散列过程为:

      (1) 存储记录时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录.

      (2) 查找记录使,通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录.

8.3.3 采用散列技术需要考虑的两个主要问题:

      (1) 散列函数的设计. 如何设计一个简单均匀存储利用率高的散列函数.

      (2) 冲突的处理. 如何采取合适的处理冲突的方法来解决冲突.


8.3.4 散列函数的设计

     ①直接定址法

   

     ②数字分析法

       

     ③平方取中法

    

     ④折叠法

    

     ⑤除留余数法

    

    

      ⑥随机数法

     

8.3.5处理冲突的方法

     (1)开放定址法(闭散列)

      ①线性参测法

      所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。

公式为:

fi(key) = (f(key)+di) MOD m (di=1,2,3,......,m-1)

  • 用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表中无待查的关键字,即查找失败。

      比如说,我们的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},表长为12。 我们用散列函数f(key) = key mod l2。

      当计算前S个数{12,67,56,16,25}时,都是没有冲突的散列地址,直接存入:

     计算key = 37时,发现f(37) = 1,此时就与25所在的位置冲突。

     于是我们应用上面的公式f(37) = (f(37)+1) mod 12 = 2。于是将37存入下标为2的位置。这其实就是房子被人买了于是买下一间的作法:。

    接下来22,29,15,47都没有冲突,正常的存入:

       到了 key=48,我们计算得到f(48) = 0,与12所在的0位置冲突了,不要紧,我们f(48) = (f(48)+1) mod 12 = 1,此时又与25所在的位置冲突。于是f(48) = (f(48)+2) mod 12=2,还是冲突……一直到 f(48) = (f(48)+6) mod 12 = 6时,才有空位,机不可失,赶快存入:

       我们把这种解决冲突的开放定址法称为线性探测法。

从这个例子我们也看到,我们在解决冲突的时候,还会碰到如48和37这种本来都不是同义词却需要争夺一个地址的情况,我们称这种现象为堆积。很显然,堆积的出现,使得我们需要不断处理冲突,无论是存入还是査找效率都会大大降低。

    ②二次探测法

考虑深一步,如果发生这样的情况,当最后一个key=34,f(key)=10,与22所在的位置冲突,可是22后面没有空位置了,反而它的前面有一个空位置,尽管可以 不断地求余数后得到结果,但效率很差。

因此我们可以改进di = 12, -12, 22, -22,……, q2, -q2 (q <= m/2),这样就等于是可以双向寻找到可能的空位置。

对于34来说,我 们取di即可找到空位置了。另外增加平方运算的目的是为了不让关键字都聚集在 某一块区域。我们称这种方法为二次探测法。

fi(key) = (f(key)+di) MOD m (di = 12, -12, 22, -22,……, q2, -q2, q <= m/2)

     ③随机探测法

还有一种方法是,在冲突时,对于位移量 di 采用随机函数计算得到,我们称之为随机探测法。

此时一定会有人问,既然是随机,那么查找的时候不也随机生成办吗?如何可以获得相同的地址呢?这是个问题。这里的随机其实是伪随机数。

伪随机数是说,如果我们设置随机种子相同,则不断调用随机函数可以生成不会重复的数列,我们在査找时,用同样的随机种子,它每次得到的数列是相同的,相同的 di 当然可以得到相同的散列地址。

fi(key) = (f(key)+di) MOD m (di是一个随机数列)

总之,开放定址法只要在散列表未填满时,总是能找到不发生冲突的地址,是我们常用的解决冲突的办法。


     (2)拉链法(开散列)

          基本思想: 

                 将所有关键码为同义词的记录存储在一个单链表中,

                 在散列表中存储所有同义词单链表的头指针

    

    

     (3)公共溢出法

     类似于拉链法,将链接存储改为数组存储

   


8.3.6 闭散列的实现(线性探测法)

Hash.h

#ifndef HASH_H

#define HASH_H

#include <stdio.h>
#include <stdlib.h>

#define maxsize 12

typedef int DataType;

typedef struct HashTable
{
    int *elem; //存储空间首地址
    int count;
}HashTable;

void InitHashTable(HashTable *H,DataType r[],int m);
int Hash(int key,int m);
void InsertHash(HashTable *H,int key,int m);
int SearchHash(HashTable *H,int key,int m);

#endif


Hash.c


#include "Hash.h"

void InitHashTable(HashTable *H,DataType r[],int m)
{
    int i;
    /*加上H的初始化地址申请,会出现错误*/
    //H = (HashTable *)malloc(sizeof(HashTable));
    H->count = m;
    H->elem = (int *)malloc(12*sizeof(int));
    /*初始化数组元素为0,0代表数组没右被占用*/
    for(i=0;i<m;i++)
    {
        H->elem[i] = 0;
    }
    /*插入*/
    for(i=0;i<m;i++)
    {
        InsertHash(H,r[i],m);
    }

}

int Hash(int key,int m)
{
    return key%m;
}

/*线性探测法进行插入*/
void InsertHash(HashTable *H,int key,int m)
{
    int j = Hash(key,m);
    while(H->elem[j] != 0)
    {
        j = (j+1)%m;
    }
    //printf("jjjjjj:%d\n",j);
    H->elem[j] = key;
    //printf("kkkkkk:%d\n",H->elem[j]);
}

int SearchHash(HashTable *H,int key,int m)
{
    printf("%d\n",H->elem[5]);
    int j = Hash(key,m);
    while(H->elem[j] != key)
    {
        /*开放地址法的线性探测法,地址+1*/
        j = (j+1)%m;
        /*如果下一个值为空,或者如果循环回到原点*/
        if(H->elem[j] == 0 || j == Hash(key,m))
        {
             return 0; /*返回0,代表查找失败*/
        }
    }
    return 1;  /*返回1,代表查找成功*/
}


main.c


#include "Hash.h"

int main(void)
{
    DataType r[] = {12,57,56,16,25,37,22,29,15,47,48,34};
    int m = 12;
    /*方式一:*/
    /*H本身是一个HashTable类型的指针变量,
     *若想使用H作为按地址传递函数的参数,
     *则必须让指针H,指向实际的内存空间(这里指向L) */
    HashTable *H;
    HashTable L;
    H = &L;

    InitHashTable(&(*H),r,m);

    if(SearchHash(&(*H),22,m)==1)
    {
        printf("Search 22 SUCCESS\n");
    }else{
        printf("Search 22 ERROR\n");
    }


    /*方式二,
   *直接取实际内存的地址作为传递值*/
    HashTable H2;

    InitHashTable(&H2,r,m);

    if(SearchHash(&H2,22,m)==1)
    {
        printf("Search 22 SUCCESS\n");
    }else{
        printf("Search 22 ERROR\n");
    }

    return 0;
}







    

0 0