哈希表

 效率                        插入删除

哈希表 O[1]
树  O[N]
链表   O[1]
有序数组 O[N]

缺点

基于数组.难于扩展.被填满时候效率低下.无法有序遍历.

 

哈希化:

一种压缩方法把数位幂的连乘系统中得到的巨大的整数范围压缩到

可接受的数组范围中。

samllNumber = largetNumber%smallRange;

这就是一种哈希函数:把一个大范围的数字哈希成一个小范围的数字.

这个小的范围对应着数组的下标.

arrayIndex = hugeNumber%arraySize;

然后使用取余操作符.把得到的巨大的整数范围转换成两倍于要储存内容的数组下标范围:

arraySize = numberWords*2;

arrayIndex = hugeNumber%arraySize;

 

 

哈希表:

使用哈希函数向数组插入数据后.这个数组就称为哈希表.

例子:

想在内存中存储50000个单词.起初可能考虑每个单词占据一个数组单元.那么数组大小50000.

同时可以使用数组下标存取单词.这样存取确实很快.

单词转换成数组下标:

*数字累加法

一个转换单词的简单方法是把单词每一个字符代码求和.

例如把单词cats转换成数字.

c=3

a=1

t=20

s=19

然后把它们相加:3+1+20+19=43.那么在字典表中.单词cats存储在数组下标为43的

单元中.所有的英文单词都用这个办法转换成数组下标.

哈希函数.把一个大范围的数字数字哈希转化成一个小范围的数字.这个小范围的数字对应着数组的下标.

arrayIndex = hugeNumber%arraySize;

*幂连乘<----确保单词中的每个字符都可以通过独一无二的方法得到最终的数字.

回忆一下:把单词每个字母乘以27的适当次幂.使单词成为一个巨大的数字.

 

 

数字折叠:

 

冲突:

把巨大的数字空间压缩成较小的数字空间.必然要付出代价.不能保证每个单词映射到数组的空白单元.

解决冲突:

开放地址法:

通过系统的方法找到数组的一个空位.把这个单词填入.

而不再用哈西函数的到数组的下标.

 *线形探测:线性查找空白单元.

find()

//find item with key

public DataItem find(int key){

//assums table not full

//hash the key

int hashVal = hashFunc(key);

//until empty cell

while(hashArray[hashVal]!=null){

       if(hashArray[hashVal].getKey()==key)

                     return hashArray[hashVal];  

  

        ++hashVal;
//wrap around if nessary hashVal
%=arraySize; } return null; }

 

//in this method i myself wanna insert a new item into the

//assumes table not full

public void insert(DataItem item)(

   //extract key

     int key = item.getKey();

  //hash the key

//until empty cell or -1

     int hashVal = hashFunc(key);



//find the propur index for the new item

     while(hashArray[hashVal]!=null &&

           hashArray[hashVal].iData!=-1

      ){

         ++hashVal;

         hashVal%=   arraySize;

       }

        hashArray[hashVal]=item;

}       

 

//delete dedecate  data item

public DataItem delete(int key){

//i got the dedecate data from the key

               int hashVal =  

            hashFunc[key];

//until the empty cell

//found the key

        while(hashArray[hashVal]!=null){

                if(hashArray[hashVal].getKey()==key){

                               DateItem temp = //save item

                                  hashArray[hashVal];

                             //leave it to empty cell

                              hashArray[hashVal] = nonItem;

                              return temp;

                }

         ++hashVal;

         hashVal %= arrraySize;

       }

}

 

 *二次探测

在线形探测中.哈希函数原始下标 x.线形探测是x+1.x+2.x+3

二次探测:x+1.x+4.x+9.x+16.x+25.

当二次探测的搜索变长时候.好像它变得越来越绝望.第一次.它查找相邻的单元.

如果这个单元被占用.它认为这里可能是一个小的聚集.所以它尝试距离为4的单元.

如果这里也被占用.它变得焦虑.认为这里有一个更大的聚集.然后它尝试距离为9的单元.

如果这里还被占用.它感到一丝恐慌.跳到距离为16的单元.很快它会歇斯底里的飞跃整个数组空间.

当哈希表几乎填满时候.就会出现这种情况.

 

二次探测的问题:

二次探测消除了在线形探测中产生的聚集问题.这种聚集叫原始聚集.然而二次探测产生了另一种.更细的聚集问题.

二次聚集.比如将184.302.420.544依次插入到表中.他们都影射到7.那么302需要以一为步长的探测.

420需要以4为步长的探测.544需要以9为步长的探测.只要一有项.其关键字影射到7.就需要更长步长的探测.

这种现象叫做二次聚集.

 

 

 *再哈希法

stepSize = constant -(key%constant);
constant是质数且小于数组容量
stepSize = 5-(key%5);

现在需要一种方法是产生一种依赖关键字的探测序列.而不是每个关键字都一样.

对于指定的关键字.步长在整个探测中是不变的.不过不同的关键字使用不同的步长.

表的容量是一个质数:

再哈西法要求表的容量是一个质数.

假设表的容量不是质数.例如假设表长是15<下标从0到14>

有一个特定关键字映射到0.步长为5.探测序列是0.

5 10 0 5 10 依次类推.一直循环.算法只尝试这三个单元.

所以不能找到某些空白单元.

例如位置1,2,3或者其他位置.算法会最终导致崩溃.

链地址法:在哈西标的每个单元中设置链表  

哈希表

效率:

查找/插入: 1+nComps -->nComps关键字比较次数

你可能感兴趣的:(哈希表)