【JavaScript数据结构与算法】散列

本文深入探讨了散列技术,介绍了散列表的原理及其在数据存储中的应用。文章详细讲解了散列函数的设计,包括简单的除留余数法和更优的霍纳算法,并对比了它们在处理字符串和整数时的性能。此外,还讨论了散列表中的碰撞问题及解决方案,如开链法和线性探测法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

散列

1 什么是散列?

散列是一种常用的数据存储技术,散列后的数据可以快速插入或取用,相应的数据结构叫做散列表。在散列表上插入、删除、取用数据都非常快,但是对于查找操作来说却效率底下。

2 散列的实现

基于数组设计,数组长度预先设定。使用散列表存储数据时,通过一个散列函数将键映射为一个数字,这个数字的范围是0到散列表的长度。键的数量无限的,数组长度有限,出现的两个键映射为同一个值的情况成为碰撞。常见的限制是数组长度为一个质数。

3 HashTable类

function HashTable(){
    this.table=new Array(137);
    this.simpleHash=simpleHash;
    this.showDistro=showDistro;
    this.put=put;
}
3.1 选择一个散列函数

散列函数的选择依赖于键值的数据类型,如果键是整形,最简单的散列函数就是以数组的长度对键取余,称为除留余数法。对于键是字符串类型的情况,将每个字符的ASCII值相加是可供参考的。

//散列函数
function simpleHash(data){
    var total=0;
    for(var i=0;i<data.length;i++){
        total+=data.charCodeAt(i);
    }
    return total%this.table.length;
}
//将数据存入散列表
function put(data){
    var pos=this.simpleHash(data);
    this.table[pos]=data;
}
//显示数据
function showDistro(){
    var n=0;
    for(var i=0;i<this.table.length;i++){
        if(this.table[i]!=undefined){
            console.log(i+":"+this.table[i]);
        }
    }
}
3.2 一个更好的散列函数

使用霍纳算法,仍然先计算字符串中各个字符的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;
    return parseInt(total);
}

测试发现无论是对于字符串还是整兴,betterHash()的散列效果都更胜一筹。

function get(key){
    return this.table[this.betterHash(key)];
}

4 碰撞处理

介绍两种碰撞解决办法,开链法和线性探测法。

4.1 开链法

指实现散列表的底层数组中,每个数组元素又是一个新的数据结构,比如另一个数组,这样就能存储多个键了。第二组数组也被成为链。

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

修改后的put()方法:既保存数据也保存键值,使用链中两个连续的单元格,第一个保存键值第二个保存数据。
修改后的get()方法:先对键值散列,根据散列后的值找到散列表中的相应位置,然后搜索该位置上的链,直到找到键值。如果找到就将其后的数据返回,否则返回undefined。

function put(key,data){
    var index=0;
    var pos=this.betterHash(key);
    while(this.table[pos][index]!=undefined){
          index++;
    }
    this.table[pos][index]=key;
    this.table[pos][++index]=data;
}
function get(key){
    var index=0;
    var pos=this.betterHash(key);
    while(this.table[pos][index]!=undefined&&this.table[pos][index]!=key){
            index+=2;
    }
    return this.table[pos][index+1];
}
4.2 线性探测法

线性探测法隶属于一种更一般化的散列技术:开放寻址散列。当发生碰撞时,检查散列表中下一个位置是否为空,如果为空,就将数据存入该位置,如果不为空,则继续检查下一个位置,知道找到一个空位置为止。该技术基于一个事实:每个散列表都会有很多空的单元格可以用于存储数据。
注意:常常按照以下方式选择解决办法-数组大小是待存储数据个数的1.5倍,使用开链法,数组大小是待存储数据的两倍及以上时,使用线性探测法。

//在HashTable构造函数中加入this.values=[]
function put(key,data){
    var pos=this.betterHash(key);
    while(this.table[pos]!=undefined){
        pos++;
    }
    this.table[pos]=key;
    this.values[pos]=data;
}
function get(key){
    var hash=this.betterHash(key);
    while(this.table[hash]!=key&&this.table[hash]!=key){
    	hash++;
    }
    return this.values[hash];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值