散列的概念和用途
# 散列的概念和用途
散列后的数据可以快速插入取用。
在散列表上插入、删除和取用数据非常快,但是查找数据却效率低下,比如查找一组数据中的最大值和最小值。
JavaScript 散列表基于数组设计,理想情况下散列函数会将每一个键值映射为唯一的数组索引,数组长度有限制,更现实的策略是将健均匀分布。
# 散列的一些关键点
数组长度是预先设定的,可以随时增加,所有元素根据和该元素对应的健,保存在数组的特定位置。
即使使用高效的散列函数,仍然存在两个键值相同的情况,这种现象称为碰撞。
数组的长度应该是一个质数,所有的策略都基于碰撞。
解决散列冲突的方法有:
开链法:两个键相同保存位置一样。开辟第二个数组,也称第二个数组为链。
线性探测法属于开放寻址散列,查找散列位置,如果当前位置没有继续寻找下一个位置。存储数据较大比较合适。
数据大小 >= 1.5*数据(开链法),数据大小 >= 2*数据(线性探测法)。
更好的散列函数:
djb2。
var djb2HashCode = function(key) {
var hash = 5381;
for (var i = 0; i < key.length; i++) {
hash = hash * 33 + key.charCodeAt(i);
}
return hash % 1013;
};
1
2
3
4
5
6
7
2
3
4
5
6
7
# 散列的代码实现
function HashTable() {
this.table = new Array(137);
this.simpleHash = simpleHash;
this.betterHash = betterHash;
this.buildChains = buildChains;
this.put = put;
this.get = get;
this.showHashTable = showHashTable;
}
// 简单的散列函数,使用除留余数法
function simpleHash(data) {
var hash = 0;
for (var i = 0; i < data.length; i++) {
hash += data.charCodeAt(i);
}
return hash % this.table.length;
}
// 分布更均匀的散列函数
function betterHash(data) {
var H = 31; // 质数
var hash = 0;
for (var i = 0; i < data.length; i++) {
hash += H * hash + data.charCodeAt(i);
}
if (hash < 0) {
hash += this.table.length - 1;
}
return hash % this.table.length;
}
// 开链法
function buildChains() {
for (var i = 0; i < this.table.length; i++) {
this.table[i] = new Array();
}
}
// 插入数据
function put(data) {
var pos = this.simpleHash(data);
this.table[pos] = data;
// var pos = this.betterHash(data);
// this.table[pos] = data;
// 开链法
// var pos = this.simpleHash(data);
// var index = 0;
// if (!this.table[pos][index]) {
// this.table[pos][index] = data;
// index++;
// } else {
// while (this.table[pos][index]) {
// index++;
// }
// this.table[pos][index] = data;
// }
// 线性探测法
// var pos = this.simpleHash(data);
// if (!this.table[pos]) {
// this.table[pos] = data;
// } else {
// while (this.table[pos]) {
// pos++;
// }
// this.table[pos] = data;
// }
}
// 取出数据
function get(data) {
return this.table[this.simpleHash(data)];
// 配合线性探测法
// var pos = this.simpleHash(data);
// console.log(data + '本来的位置是:' + pos);
// for (var i = pos; i < this.table.length; i++) {
// if (this.table[i] === data) {
// return i;
// }
// }
// return -1;
}
// 打印整个散列表的数据
function showHashTable() {
var n = 0;
for (var i = 0; i < this.table.length; i++) {
if (this.table[i]) {
console.log("键 " + i + " 对应的值是:" + this.table[i]);
}
}
// 配合开链法
// var n = 0;
// for (var i = 0; i < this.table.length; i++) {
// if (this.table[i][0]) {
// console.log('键 ' + i + ' 对应的值是:' + this.table[i]);
// }
// }
}
var hashTable = new HashTable();
// 开链
// hashTable.buildChains();
hashTable.put("first");
hashTable.put("study");
hashTable.put("student");
hashTable.put("cool");
hashTable.put("ice");
hashTable.put("china");
hashTable.put("nicha");
// console.log('使用线性探测法之后 nicha 的位置是:' + hashTable.get('nicha'));
hashTable.showHashTable();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121