數據結構之哈希表

數據結構之哈希表
1.哈希表簡介
2.衝突
3.重載因子
4.一些常用的Hash算法
1.先來看看哈希表在百度百科的解釋,哈希表是根據關鍵碼值而直接經行訪問的數據結構。也就是說,他通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數。
其中有些關鍵字:關鍵值...數據結構....映射.....查找速度
簡單的說,哈希表是一種比普通查找方法查找起來更快的數據結構,他是一種數據結構,他的存儲方法是通過一組數據(key)的映射來決定的,所以只要能得到這組數據,那麼就能根據自己設定的映射方式(一般我們叫做哈希函數)來直接算出該數據存儲的位置,以達到快速查找的目的。利用這種存儲結構,可以更快的查找到我們要查找的數據。
1.衝突,由於存儲地址是有限的和固定的,那麼在我們的映射方式(哈希函數)對於多個key值經行映射時,就會可能有多個key值同時算出來一個地址,這種現象叫做衝突,解決衝突的方式很多,簡要介紹幾種方式
1.再散列法,就是在發生衝突時,將容器改變,重新經行哈希,直到不產生衝突爲止。
2.鏈地址法,在產生衝突時,在原來的地址上建立一個鏈表。
3.建立公共溢出區,把產生衝突的數據,全部儲存到一塊區域裏去。
這裏主要講鏈地址法。
2.重載因子,在鏈地址法處理衝突時,其實是用空間換取時間,數組下面建立鏈表,這樣既擁有了數組的方便尋址,又有鏈表的方便增刪。衝突的頻率過於頻繁,而當衝突同時發生在同意區域時,其實就形成了一支鏈表,這樣就根本不能達到減少查找時間的效果,所以我們要控制某一支鏈表的長度,當某一支鏈表超過某個比例時,我們應當做某些處理,以減少查找時間,增加查找效率。這個比例我們乘坐重載因子,當某一支鏈表的長度比上整個數組的的長度大於重載因子時,將數組擴容,所有元素重新排列,稱之爲再哈希。這個重載因子決定了查找的效率,太大查找效率會偏低,太小會導致再哈希過於頻繁,浪費時間。據科學家計算過,當重載因子爲0.75時,算法效率最高。
3.哈希算法
1.直接尋址法 根據key值用某個線性函數獲取地址
2.數字分析法 利用重複數字較少的部分構成散列地址,減少衝突
3.平法取中法 關鍵字平方後去中間幾位作爲散列地址
4.除留取餘法 取某個值p,用key值除p得到的值取餘數作爲散列地址
5.隨機數法 取一組隨機數作爲散列地址
這些方法是平時一般比較常用的方法,說到這裏的話,我想大家都已經懂了,利用key值映射得到地址,然後去相應的地址裏找。就像是一道腦筋急轉彎,我告訴你老師所在的樓層是key的三倍加一,然後告訴你key是2,你能知道老師在那個樓層麼,2是已經給出的key,3x+1是哈希函數,利用函數尋找地址就是這個意思。ok,獻上一段鏈地址法的代碼,哈希函數是平方取餘
public class Hash {
private Maping[] array ; //存儲數組
private double factor = 0.7; //裝載因子
private int count = 10; //默認裝載個數
private int enough = 6; //超出後的添加數

/**
* 使用默認構造器,構造一個Hash
*/
public Hash(){
array = new Maping[count];
}
/**
* 使用自定義的構造器,構造Hash
* @param count 自定義的初始裝載個數
* @param factor 自定義的裝載因子
*/
public Hash(int count,float factor){
this.count = count;
this.factor = factor ;
array = new Maping[count];
}
/**
* 跟據數據的key值查找
* @param key 映射的key值
* @return 返回元素的value值,如果不存在,返回null
*/
public Object searchValue(int key){
//計算出該元素所存在的數組索引
int code1 = getHashCode(key);
//根據索引所在的數組元素,往鏈表之下尋找
Maping temp = array[code1];
while(temp != null){
//如果找到元素,返回value值
if(temp.getData() == key){
return temp.getValue();
}//如果沒找到,繼續往下找
else{
temp = temp.getNext();
}
}
return null;
}
/**
* 添加一個數據進入Hash,該Hash允許相同的value值的存在,未處理相同key值的存在(原則上講不允許相同的key值)
* @param m 要添加的元素
*/
public void add(Maping m){
int xCount = 1; //記錄該條鏈上所擁有的元素個數
//利用自己寫的Hash函數,獲取在數組中應存放的位置
int code1 = getHashCode(m.getData());
//如過爲空,將他放在裏邊
if(array[code1]==null){
array[code1] = m;
}//如果不爲空
else{
//一直找到鏈表的末尾,將新添加的元素放在隊尾
Maping temp = array[code1];
xCount++;
while(temp.getNext() != null){
xCount++;
temp = temp.getNext();
}//添加進入隊尾
temp.setNext(m);
m.setNext(null);
}

//如果鏈表長度超過了比例,也就是超過重載因子的限制,那麼就重新排列reHash
if((double)xCount/(double)count>factor){
reHash();
}
}
/**
* 利用自己寫的Hash函數,獲取應存放的位置
* 其實這只是一個簡單的映射,這種映射可以隨自己的意願更改
* @param data 函數的利用值
* @return 返回得到的Code值
*/
public int getHashCode(int data){
//平方取餘法
int value = (data*data)%count;
return value;
}
/**
* 由於裝載數超過裝載因子數目,重新把元素提出,將數組擴容,再次Hash
*/
public void reHash(){
//將已有的Hash中的數據全部存儲起來
List<Maping> list = new ArrayList<Maping>();
for(int i=0;i<array.length;i++){
Maping temp = array[i];
while(temp != null){
Maping temp1 = temp.getNext();
//把得到的節點隔離出來
temp.setNext(null);
list.add(temp);
//賦值,還原指針
temp = temp1;
}
}
//重置Hash數組,並改變容量
count = count + enough;
array = new Maping[count];
//重新添加
for(int i=0;i<list.size();i++){
Maping m = list.get(i);
add(m);
}
}
/**
* 打印當前的Hash表(測試用)
*/
public void print(){
for(int i=0;i<array.length;i++){
System.out.println(i+"---->");
if(array[i]==null){
continue;
}
else {
Maping temp = new Maping(0, "temp");
temp = array[i];
while(temp != null){
System.out.println(temp.getData()+" "+temp.getValue());
temp = temp.getNext();
}
}
System.out.println();
}
}
}
發佈了28 篇原創文章 · 獲贊 0 · 訪問量 3359
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章