【DS】C8 -- 散列

149 阅读3分钟

C8 -- 散列

散列使用的数据结构叫做散列表。在散列表上插入、删除和取用数据都非常快,但查找操作效率低下。我们的散列表是基于数组进行设计的。使用散列表存储数据时,通过一个散列函数将键映射为一个数字,这个数字是0到散列表的长度。理想情况下,散列函数会将每个键值映射为一个唯一的数组索引。然而,键的数量是无限的,数组的长度是有限的,应该让散列函数尽量将键均匀地映射到数组中。存在将两个键映射成同一个值的可能,这种现象被称为 :碰撞。

散列表中的数组大小的限制,数组的长度应该是一个质数。确定数组大小的策略,都基于处理碰撞的技术。

1.HashTable类

function HashTable(){
	this.table = new Array(137);
	this.simpleHash = simpleHash;
	this.showDistro = showDistro;
	this.put = put;
	//this.get = get;
}

function put(data){
	var pos = this.simpleHash(data);
	this.table[pos] = data;
}

function simpleHash(data){
	var total = 0;
	for(var i = 0;i<data.length;++i){
		total += data.charCodeAt(i);
	}
	return total % this.table.length;
}

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

除留余数法:如果键是整型,最简单的散列函数就是以数组的长度对键取余。(数组的长度要求是质数)

如果键是字符串类型,将字符串中每个字符的ASCII码值相加除留余数。

一个更好的散列函数

为了避免碰撞,首先要确保散列表中用来存储数据的数组其大小是个质数。

霍纳算法:

function betterHash(string,arr){
	const H = 37;
	var total = 0;
	for(var i = 0;i<string.length;++i){
		total += H * total + string.charCodeAt(i); 
	}
	total = total % arr.length;
	if(total < 0){
		total += arr.length-1;
	}
	return parseInt(total);
}

对散列表排序、从散列表中取值

function put (key,data){
	var pos = this.betterHash(key);
	this.table[pos] = data;
}
function get(key){
	return this.table[this.betterHash(key)];
}

碰撞处理

1.开链法

二维数组

function buildChains(){
	for(var i = 0;i<this.table.length;++i ){
		this.table[i] = new Array();
	}
}

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

//既保存数据也保存键值
function put(key,data){
	var pos =this.betterHash(key);
	var index = 0;
	if(this.table[pos][index] == undefined){
		this.table[pos][index+1] = data;
	}
	++index;
	else{
		while (this.table[pos][index] != undefined){
			++index;
		}
		this.table[pos][index+1] = data;
	}
}

function get(key){
	var index =0;
	var hash = this.betterHash(key);
	if(this.table[pos][index] = key){
		return this.table[pos][index+1];
	}
	index +=2;
	else{
		while(this.table[pos][index] != key){
			index +=2;
		}
		return this.table[pos][index+1];
	}
	return undefined;
}
2.线性探测法

如果数组的大小是待存储数据个数的1.5倍,那么使用开链法。如果数组的大小是待存储数据的两倍及两倍以上,那么使用线性探测法。开放寻址散列,当发生碰撞时,线性探测法检查散列表中下一个位置是否为空。如果为空,就将数据存入该位置;如果不为空就继续检查下一个位置,直到找到一个空的位置为止。

数组table和values并行工作,当将一个键值保存到数组table中时,将数据存入数组values中对应的位置。

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

function get(key){
	var hash = -1;
	hash  = this.betterHash(key);
	if(hash>-1){
		for(var i = hash;this.table[hash] !=undefined;i++){
			if(this.table[hash] == key){
				return this.values[hash];
			}
		}
	}
	return undefined;
}