1. 程式人生 > >第八章雜湊

第八章雜湊

雜湊的含義

散列表(Hash table,也叫雜湊表),是根據關鍵碼值(Key value)而直接進行訪問的資料結構。也就是說,它通過把關鍵碼值對映到表中一個位置來訪問記錄,以加快查詢的速度。這個對映函式叫做雜湊函式,存放記錄的陣列叫做散列表。

對不同的關鍵字可能得到同一雜湊地址,即k1≠k2,而f(k1)=f(k2),這種現象稱為碰撞(英語:Collision)。具有相同函式值的關鍵字對該雜湊函式來說稱做同義詞。綜上所述,根據雜湊函式f(k)和處理碰撞的方法將一組關鍵字對映到一個有限的連續的地址集(區間)上,並以關鍵字在地址集中的“像”作為記錄在表中的儲存位置,這種表便稱為散列表,這一對映過程稱為雜湊造表或雜湊,所得的儲存位置稱雜湊地址。

雜湊函式

(1)直接定址法

   取關鍵字的某個線性函式為雜湊地址,Hash(Key)= Key 或 Hash(Key)= A*Key + B,A、B為常數。

(2)除數留餘法

  設散列表中允許的地址數為m,取一個不大於m,但接近或者等於m的質數p作為除數,按照雜湊函式:Hash(key) = key mod p  p<=m,將關鍵碼轉換成雜湊地址。對p的選擇很重要,一般取素數或m,若p選的不好,容易產生同義詞。

(3)平方取中法

  平方後取中間的,每位包含資訊比較多。假設關鍵字是1234,那麼它的平方就是1522756,再抽取中間的3位就是227作為雜湊地址;再比如關鍵字是4321,那麼它的平方就是18671041,抽取中間的3位就可以是671或者710用作雜湊地址。

(4)摺疊法

有時關鍵碼所含的位數很多,採用平方取中法計算太複雜,則可將關鍵碼分割成位數相同的幾部分(最後一部分的位數可以不同),然後取這幾部分的疊加和(捨去進位)作為雜湊地址,這方法稱為摺疊法。

比如:關鍵字是9876543210,散列表表長為三位,我們將它分成四組987|654|321|0|,然後將它們疊加求和987+654+321+0=1962,再求後3位得到雜湊地址為962。有時可能這還不能夠保證分佈均勻,不妨從一段向另一端來回摺疊後對齊相加。比如將987和321反轉,再與654和0相加,程式設計789+654+123+0=1566,此時的雜湊地址為566。

雜湊衝突

解決衝突的技術可以分為兩類:開雜湊方法(open hashing,也稱單鏈方法,separate chaining)和閉雜湊方法(closed hashing,也稱開地址方法,open addressing)。開雜湊方法解決衝突是將衝突記錄在表外,而閉雜湊方法是將衝突記錄在表內的另一個空槽。

(1)開雜湊方法:運用單鏈表儲存方式,不產生堆積現象,但因為附加了指標域而增加了空間開銷。

       

程式碼如下:

function HashTable()
{
	this.table=new Array(137);
	this.simpleHash=simpleHash;
	this.betterHash=betterHash;
	this.showDistro=showDistro;
	this.put=put;
	this.get=get;
	this.buildChains=buildChains;//開鏈法
}

function put(key,data){
	// let pos=this.simpleHash(key);
	let pos=this.betterHash(key);
	let index=0;
	while(this.table[pos][index]!=undefined){
		index++;
	}

	this.table[pos][index]=data;
}

function get(key){
	// let pos=this.simpleHash(key);
	let pos=this.betterHash(key);
	let index=0;
	if(this.table[pos][index]==key){
		return this.table[pos][index];
	}else{
		while(this.table[pos][index]&&this.table[pos][index]!=key){
			index++;
		}
		return this.table[pos][index];
	}

}

//容易發生碰撞
function simpleHash(data){
	let total=0;
	for(let i=0;i<data.length;i++){
		total +=data.charCodeAt(i);
	}

	return total%this.table.length;
}

//使用霍納演算法的雜湊函式
function betterHash(string){
	const H=37;
	let total=0;
	for(let i=0;i<string.length;i++){
		total +=H*total+string.charCodeAt(i);
	}
	total=total%this.table.length;
	if (total<0) {
		total +=this.table,length-1;
	}
	return parseInt(total);
}


function showDistro(){
	let n=0;
	for(let i=0;i<this.table.length;i++){
		if (this.table[i][0] !=undefined) {
			console.log(i+":"+this.table[i]);
		}
	}
}

//開鏈法
function buildChains(){
 for(let i=0;i<this.table.length;i++){
 	this.table[i]=new Array();
 }
}


//測試
let someNames=["David","Jennifer","Donnie","Raymond",
               "Cynthia", "Mike", "Clayton", "Danny", 
               "Jonathan","Tom","Jack","Divad","Caylton"];

let hTable=new HashTable();
hTable.buildChains();
for(let i=0;i<someNames.length;i++){
    hTable.put(someNames[i],someNames[i]);
}
hTable.showDistro();

(2)閉雜湊方法:運用順序表儲存,儲存效率較高,但容易產生堆積,查詢不易實現,需要用到二次再查詢。閉雜湊方法有桶式雜湊、線性探查、二次探查、雙雜湊方法。

//線性探測法
function HashTable()
{
	this.table=new Array(137);
	this.values=[];
	this.betterHash=betterHash;
	this.showDistro=showDistro;
	this.put=put;
	this.get=get;
}

function put(key,data){
	let pos=this.betterHash(key);
	while(this.table[pos]!=undefined){
		pos++;
	}

	this.table[pos]=key;
	this.values[pos]=data;
}

function get(key){
	let pos=this.betterHash(key);
	if(this.table[pos]==key){
		return this.values[pos];
	}else{
		while(this.table[pos]&&this.table[pos]!=key){
			pos++;
		}
		return this.values[pos];
	}

}

//使用霍納演算法的雜湊函式
function betterHash(string){
	const H=37;
	let total=0;
	for(let i=0;i<string.length;i++){
		total +=H*total+string.charCodeAt(i);
	}
	total=total%this.table.length;
	if (total<0) {
		total +=this.table,length-1;
	}
	return parseInt(total);
}


function showDistro(){
	let n=0;
	for(let i=0;i<this.table.length;i++){
		if (this.table[i] !=undefined) {
			console.log(i+":"+this.values[i]);
		}
	}
}

//測試
let someNames=["David","Jennifer","Donnie","Raymond",
               "Cynthia", "Mike", "Clayton", "Danny", 
               "Jonathan","Tom","Jack","Divad","Caylton"];

let hTable=new HashTable();
for(let i=0;i<someNames.length;i++){
	hTable.put(someNames[i],someNames[i]);
}
hTable.showDistro();
console.log(hTable.get('Town'));//undefined
console.log(hTable);