什么是字典结构?
字典
是以键值对形式存储数据的数据结构,就像电话号码薄里的名字和电话号码那样的一一对应的关系。
javascript
的Object
类就是以这样的一种字典形式设计的。
键值对在字典中以这样的方式标记:d = {key1 : value1, key2 : value2 }。
字典中的键/值对是没有顺序的。如果你想要一个特定的顺序,那么你应该在使用前自己对它们排序。
Dictionary类
Dictionary
类的基础是Array
类,而不是Object
类。我们想对字典中的键排序,而在js中是不能对 对象 的属性进行排序的。话虽如此,但在js
中一切皆对象,数组也是对象。以下面的代码开始定义Dictionary
类:
<script type="text/javascript">
function Dictionary(){
this.datastore = new Array();
}
</script>
先来定义add()
方法。该方法接受两个参数:键和值
。键是值在字典中的索引,代码如下:
function add(key,value){
this.datastore[key] = value;
}
接下来定义find()
方法,该方法以 键
做为参数,返回和其关联的值。代码如下:
function find(key){
return this.datastore[key];
}
从字典中删除键-值对 需要使用js
中的delete
函数。该函数是Object
类的一部分,该函数同时删掉键和与其关联的值:
function remove(key){
delete this.datastore[key];
}
最后,我们希望可以显示字典中所有的键-值对,可以使用如下的方法:
function showAll(){
for(var key in Object.keys(this.datastore)){
print(key + "->" + this.datastore[key]);
}
}
Dictionary类的辅助方法
我们可以定义一些在特定情况下有用的辅助方法。比如要知道字典中的元素个数可以定义一个count()
方法,代码如下:
function count(){
var n=0;
for(var key in Object.keys(this.datastore)){
++n;
}
return n;
}
为什么不使用length
属性?这是因为当键的类型为字符串时,length
属性就不管用了
还可以定义一个clear清除方法:
function clear(){
for each(var key in Object.keys(this.datastore)){
delete this.datastore[key];
}
}
备注:
for each in
(IE6,7,8不支持)无法获得对象的属性名,只能获取到属性值。
另外,遍历对象也尽量使用for in
而不是for each
,而遍历数组的话还是使用for
循环吧
for each in
无法获得对象的属性名,只能获取到属性值。
散列(hash)
什么是哈希表?
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表的做法其实很简单,就是把Key
通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
而当使用哈希表进行查询的时候,就是再次使用哈希函数将key
转换为对应的数组下标,并定位到该空间获取value
,如此一来,就可以充分利用到数组的定位性能进行数据定位
散列表的查找步骤
当存储记录时,通过散列函数计算出记录的散列地址
当查找记录时,我们通过同样的是散列函数计算记录的散列地址,并按此散列地址访问该记录
在js
中,散列函数会将每个键值映射为一个唯一的数组索引。然而,键的数量是无限的,数组的长度是有限的,所以,应该让散列函数尽量将键均匀地映射到数组中。
哈希表也是种数据结构,它可以提供快速的插入操作和查找操作。哈希表运算速度极快,哈希表的速度明显比树快,树的操作通常需要O(N)的时间级。哈希表不仅速度快,编程实现也相对容易。
哈希表算法
哈希表最常见的例子是以学生学号为关键字的成绩表,1号学生的记录位置在第一条,10号学生的记录位置在第10条...
如果我们以学生姓名为关键字,如何建立查找表,使得根据姓名可以直接找到相应记录呢?
用上述得到的数值作为对应记录在表中的位置,得到下表:
上面这张表即哈希表。
如果将来要查李秋梅的成绩,可以用上述方法求出该记录所在位置:
李秋梅:lqm 12+17+13=42
取表中第42条记录即可。
HashTable类
我们使用一个类来表示散列表,该类包含计算散列值的方法、向散列中插入数据的方法、从散列表中读取数据的方法、显示散列表中数据分布的方法等。HashTable
类的构造函数定义如下:
function HashTable(){
this.table = new Array(137);//设定数组长度为137,质数
this.simpleHash = simpleHash;
this.showDistro = showDistro;
this.put = put;
}
散列函数的选择依赖于键值的数据类型。如果键是整形,最简单的散列函数就是以数组的长度对键取余。
使用一个简单的散列函数做散列:
load("HashTable.js");
var someNames = ['David','Jennifer','Donnie','Raymond','Cynthia','Mike','Clayton','Danny','Jonathan'];
var hTable = new HashTable();
for(var i = 0;i < someNames.length;i++){
hTable.put(someNames[i]);
}
hTable.showDistro();
输出如下:
35:Cynthia
45:Clayton
57:Donnie
77:David
95:Danny
116:Mike
132:Jennifer
134:Jonathan
simpleHash()
函数通过使用js
的charCodeAt()
函数,返回每个字符的ASCII
码值,然后再将它们相加得到散列值。put
方法通过调用simpleHash()
函数得到数组的索引,然后将数据存储到该索引对应的位置上。
一个更好的散列函数
为了避免碰撞,首先要确保散列表中用来存储数据的数组其大小是个质数,这和计算散列值时使用的取余运算有关。数组的长度应该在100以上,这是为了让数据在散列表中分布得更均匀
散列化整型键
这里我们使用一个展示学生成绩的数据集,将随机产生一个9位数的键,用以识别学生身份和一门成绩,下面是产生学生数据(包含ID和成绩)的函数:
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;
}
}
使用getRandomInt()
函数时,可以指定随机数的最值。genStuData()
函数生成学生的数据。里层的循环用来生成学生的ID,紧跟在循环后面的代码生成一个随机的成绩,并把成绩弄在ID的后面。主程序会把ID和成绩分离。散列函数将学生ID里的数字相加,使用simpleHash()
函数计算出散列值。
对散列表排序
put
方法同时接受键和数据作为参数,对键值散列后,将数据存储到散列表中:
function put(key,data){
var pos = this.betterHash(key);
this.table[pos] = data;
}
put
方法将键值散列化后,将数据存储到散列化后的键值对应在数组中的位置上。
从散列表中取值
定义get()
方法,用以读取存储在散列表中的数据。该方法同样需要对键值进行散列化,然后才能知道数据存储在数组的什么位置,代码如下:
function get(key){
return this.table[this.betterHash(key)];
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。