您的位置:首页 > 理论基础 > 数据结构算法

JavaScript数据结构 --- 散列

2015-12-01 00:22 309 查看

JavaScript数据结构 --- 散列

散列是一种常用的数据存储技术,散列后的数据可以快速地插入或取用,但查找操作却效率低下,这是可以依靠其他数据结构(例如二叉查找树)。

散列使用的数据结构叫散列表,这里的散列表是基于数组进行设计的。所有元素根据该元素对应的(类型字典中的键),保存在数组的特定位置。使用散列表存储数据时,通过一个散列函数 将键映射为一个长度为 0 到散列表长度的数字。

一般来说,数组的长度是有限的,所以我们让散列函数尽量将键均匀地映射到数组中。



1. HashTable 类

使用一个类来表示散列表。

function HashTable() {
this.table = new Array(137); // 构建一个新数组用来存储 散列表
this.simpleHash = simpleHash; // 一个简单的散列函数
this.showDistro = showDistro; // 显示散列表中的数据
this.put = put; // 将数据插入散列表
}

1.1 选择散列函数

散列函数的选择依赖键值的数据类型。如果键是整形,最简单的散列函数就是以数组的长度对键取余。当很多键值是数组的长度的倍数时,这种方式会导致大规模碰撞(散列值一样)。所以一般将数组的长度设为质数。

针对字符串类型的散列函数比较困难,要更加小心选择。

我们定义的 smpleHash 函数是将每个字符的 ASCII 码相加。如此散列值就是 ASCII 码值之和除以数组长度得到的余数。

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

再定义用来将数据存入散列表的方法 put() 和用来显示散列表中的数据的方法 showDistro() 。

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]);
}
}
}

测试用例

HashTable1

程序结果:

35: Cynthia
45: Clayton
57: Donnie
77: David
95: Danny
116: Mike
132: Jennifer
134: Jonathan

根据结果来看,数据没有均匀分布,有时候还会出现碰撞(散列值一样)的情况,我们需要改善散列函数。

1.2 更好的散列函数

使用 霍纳德算法 得到更好的散列函数。

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

结果为:

12: Jennifer
22: Raymond
55: Donnie
58: Clayton
80: Jonathan
82: Mike
103: Cynthia
110: Danny

1.3 散列化整型键

这里使用的数据集是学生的成绩,随机产生一个9位数的键,用来识别学生身份和一门成绩。

function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}

function genStuData(arr) {
for (var i = 0; i < arr.length; ++i) {
var num = "";
for (var j = 1; j <= 9; ++j) {
num += Math.floor(Math.random() * 10);
}
num += getRandomInt(50,100); //可以指定随机数的最大值和最小值
arr[i] = num;
}
}

genStuDate() 函数生成学生的数据。里层的循环生成 ID,后面的循环代码生成一个随机的成绩,并把成绩添加在ID后面。主程序会分离ID和成绩,散列函数将学生ID里的数字相加,使用 simpleHash() 函数计算出散列值。

示例程序:

HashTable2

运行结果为:

Student data:

04437992 89
35054068 96
34780034 52
34103157 91
29752520 74
96057108 80
55413450 57
33664847 61
33179680 71
91022430 51

Data distribution:

9: 91022430251
16: 34103157291
18: 34780034252
26: 29752520374
27: 55413450857
28: 33664847061
30: 96057108680
32: 33179680771
35: 35054068996
44: 04437992989

使用更好的 散列函数 betterHash(),得到的输出。

示例程序:

betterTable2.sj

Student data:

90605894 100
41483254 96
42802463 86
25395996 90
39102248 97
52528869 68
53083791 94
01904985 63
51172821 61
84746600 82

Data distribution:

19: 84746600382
40: 39102248697
45: 01904985263
61: 53083791894
64: 42802463486
70: 25395996790
93: 52528869068
105: 41483254496
112: 906058948100
127: 51172821561

通过对比,可以发现无论是字符串还是整型, betterHash() 的散列效果都更胜一筹。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: