java哈希查找算法_【Java】 大話數據結構(13) 查找算法(4) (散列表(哈希表))...

本文根據《大話數據結構》一書,實現了Java版的一個簡單的散列表(哈希表)。

基本概念

對關鍵字key,將其值存放在f(key)的存儲位置上。由此,在查找時不需比較,只需計算出f(key)便可直接取得所查記錄。這個函數 f() 就叫做散列函數,按這個思想建立的表稱為散列表。

散列技術即是一種存儲方法,又是一種查找方法:

存儲過程:根據關鍵字key,算出f(key),將記錄存放在f(key)的位置上;

查找過程:根據關鍵字key,算出f(key),該位置上的值即為要找的記錄。

散列函數的構造方法

直接定址法

直接取關鍵字的線性函數為散列地址:f(key)=a×key+b(a,b為常數)

如:對下表的記錄,關鍵字key取為出生年份,令f(key)=key-1980即可。

362985882054b6fad2353414c138159d.png

數字分析法

分析一組數據,找出其規律,盡可能利用這些數據來構造沖突幾率較低的散列地址

如:以員工的手機號碼作為關鍵字,前7位數字基本相同,可以選擇后面四位數字作為散列地址。

平方取中法

當無法確定關鍵字中哪幾位分布較均勻時,可以先求出關鍵字的平方值,然后按需要取平方值的中間幾位作為散列地址。

折疊法

將關鍵字分割成位數相同的幾部分,最后一部分位數可以不同,然后取這幾部分的疊加和(去除進位)作為散列地址。

除留余數法

最為常用的方法,取關鍵字被某個不大於散列表表長m的數p除后所得的余數為散列地址。

f(key) = key MOD p,p<=m。

隨機數法

選擇一隨機函數(偽隨機),取關鍵字的隨機值作為散列地址,通常用於關鍵字長度不同的場合。

處理散列沖突的方法

當兩個關鍵字key1和key2不同時,有f(key1)=f(key2),這種現象稱為沖突。一般情況下,我們會盡量設計恰當的散列函數減少沖突,但無法完全避免,這就需要對沖突進行處理。

開放尋址法

一旦發生沖突,就去尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到,並將記錄存入。根據下一個位置的不同,又可分為以下三種:

①線性探測法:

fe28c90988ed51b3a6b95a9738414966.png

②二次探測法

b42e8d0a27cfb247811aefa8e01b4859.png

③隨機探測法

ec19e146af435539586108914ff47874.png

再散列函數法

在同義詞產生地址沖突時計算另一個散列函數地址,直到沖突不再發生,這種方法不易產生“聚集”,但增加了計算時間。如下圖所示(RHi代表不同的散列函數):

a71beea5b25310e2dd11a861a5089fbd.png

鏈地址法

相同地址的記錄存放在一個單鏈表中,散列表值存儲所有同義詞子表的頭指針。如下圖所示:

4164e011c46b1be732478a0a1f6e0f27.png

公共溢出區法

為所有沖突的關鍵字建立一個公共的溢出區來存放。

215c7ce55136e3eb61bdb414cb78e1f0.png

代碼實現

接下來建立一個簡單的散列表,其散列函數采用上述的除留余數法,處理沖突的方法采用開放定址法下的線性探測法。

Java代碼如下:

package HashTable;

/**

* 散列表

* @author Yongh

*

*/

public class HashTable {

int[] elem;

int count;

private static final int Nullkey = -32768;

public HashTable(int count) {

this.count = count;

elem = new int[count];

for (int i = 0; i < count; i++) {

elem[i] = Nullkey; // 代表位置為空

}

}

/*

* 散列函數

*/

public int hash(int key) {

return key % count; // 除留余數法

}

/*

* 插入操作

*/

public void insert(int key) {

int addr = hash(key); // 求散列地址

while (elem[addr] != Nullkey) { // 位置非空,有沖突

addr = (addr + 1) % count; // 開放地址法的線性探測

}

elem[addr] = key;

}

/*

* 查找操作

*/

public boolean search(int key) {

int addr = hash(key); // 求散列地址

while (elem[addr] != key) {

addr = (addr + 1) % count; // 開放地址法的線性探測

if (addr == hash(key) || elem[addr] == Nullkey) { // 循環回到原點或者到了空地址

System.out.println("要查找的記錄不存在!");

return false;

}

}

System.out.println("存在記錄:" + key + ",位置為:" + addr);

return true;

}

public static void main(String[] args) {

int[] arr = { 12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34 };

HashTable aTable = new HashTable(arr.length);

for (int a : arr) {

aTable.insert(a);

}

for (int a : arr) {

aTable.search(a);

}

}

}

bdb39611159cc2a4491eab049b54e08f8.jpg

bdb39611159cc2a4491eab049b54e08f9.jpg

存在記錄:12,位置為:0存在記錄:67,位置為:7存在記錄:56,位置為:8存在記錄:16,位置為:4存在記錄:25,位置為:1存在記錄:37,位置為:2存在記錄:22,位置為:10存在記錄:29,位置為:5存在記錄:15,位置為:3存在記錄:47,位置為:11存在記錄:48,位置為:6存在記錄:34,位置為:9

HashTable

代碼中重點可以看:插入操作是如何處理沖突 以及查找操作是如何判斷記錄是否存在的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值