不是新华字典哦,这里的字典也称作_映射_,是一种数据结构,跟set集合很相似的一种数据结构,都可以用来存储无序不重复的数据。不同的地方是集合以[值,值]的形式存储,而字典则是以[键,值]或者叫作{key:value}的形式。
先实现一个构造函数:
functionDictionary(){varitems={}}字典需要实现以下方法:
因为后面的方法都要用到has,所以先实现这个方法
this.has=function(key){//书上用的是in操作符来判断,但是in对于继承来的属性也会返回true,所以我换成了这个returnitems.hasOwnProperty(key)}实现set没啥好说的,简单的赋值
this.set=function(key,value){items[key]=value}实现remove首先判断key是否属于该字典,然后再删除
this.remove=function(key){if(this.has(key)){deleteitems[key]returntrue}returnfalse}实现values返回由数值组成的数组
this.values=function(){varvalues=[]for(varkinitems){if(items.has(k)){values.push(items[k])}}returnvalues}实现其他的方法其他的比较简单,和集合的方法实现类似,我就直接贴源代码了
this.keys=function(){returnObject.keys(items)}this.size=function(){returnObject.keys(items).length}this.clear=function(){items={}}this.getItems=function(){returnitems}this.get=function(key){returnthis.has(key)items[key]:undefined}源代码在此:
关于散列表的定义,这里摘抄一下维基百科的解释:
散列表(Hashtable,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。
可以理解为,数据中的键通过一个散列函数的计算后返回一个数据存放的地址,因此可以直接通过地址来访问它的值,这样的查找就非常快。
先看这个构造函数,注意:存储数据用的是数组,因为寻址访问元素最方便的还是数组了。
functionHashTable(){vartable=[]}需要实现的方法:
把键名的每个字母转成ASCII码再相加起来,最后和一个任意的数求余,得到数据存储的地址。
varloseloseHashCode=function(key){varhash=0for(vari=0;i this.put=function(key,value){varposition=loseloseHashCode(key)console.log(position+'-'+key)table[position]=value}this.remove=function(key){table[loseloseHashCode(key)]=undefined}this.get=function(key){returntable[loseloseHashCode(key)]}//用来输出整个散列表,查看结果用的this.print=function(){for(vari=0;i varhash=newHashTable()hash.put('Gandalf','gandalf@email.com')hash.put('John','johnsnow@email.com')hash.put('Tyrion','tyrion@email.com')//输出结果//19-Gandalf//29-John//16-Tyrion好像没什么不对,但是仔细想想:如果在某些情况下,散列函数根据传入的键计算得到的地址是一样的会怎么样呢? 看看下面这个例子: varhash=newHashTable()hash.put('Gandalf','gandalf@email.com')hash.put('John','john@email.com')hash.put('Tyrion','tyrion@email.com')hash.put('Aaron','aaron@email.com')hash.put('Donnie','donnie@email.com')hash.put('Ana','ana@email.com')hash.put('Jonathan','jonathan@email.com')hash.put('Jamie','jamie@email.com')hash.put('Sue','sue@email.com')hash.put('Mindy','mindy@email.com')hash.put('Paul','paul@email.com')hash.put('Nathan','nathan@email.com')//输出结果//19-Gandalf//29-John//16-Tyrion//16-Aaron//13-Donnie//13-Ana//5-Jonathan//5-Jamie//5-Sue//32-Mindy//32-Paul//10-Nathan这种情况就比较复杂了:Tyrion和Aaron的值都是16,Donnie和Ana都是13,还有其他很多重复的值,这时散列表表中是什么情况呢 hash.print()//输出结果//5:sue@email.com//10:nathan@email.com//13:ana@email.com//16:aaron@email.com//19:gandalf@email.com//29:john@email.com//32:paul@email.com很明显,后面的元素会覆盖前面的元素,但这样是不行的,要想办法解决冲突 目前主流的方法主要是两种:分离链接法和线性探查法,这里就简单介绍一下分离链接法。 思路很简单:你不是重复了吗,那我就在同一个位置里面放一个链表,重复的数据全都放在链表里面,你要找数据就在链表里面找。 如果不理解,可以参见下图: 根据这个思路,我们重新实现一下三个方法,不过在这之前,我们需要一个对象来保存键值对 varValuePair=function(key,value){this.key=keythis.value=value//重写toString主要是方便输出查看this.toString=function(){return'['+this.key+'-'+this.value+']'}}接下来就是重写方法了 第二种办法思路更粗暴:你不是占了这个位置嘛,那我就占下一个,下个位置还被占了?那就占再下一个~ 具体的实现我就不贴出来了,读者可以自行思考并实现,然后对照代码看看。 这里先把源代码放出来了 以上是哈希表的两个冲突解决办法,但实际上应用哈希表的时候能避免冲突就尽量避免冲突,一开始的散列函数不是一个好的函数,因为冲突太多了,这里就贴书上的一个不错的散列函数(社区),原理大致是:相加所得的hash数要够大,且尽量为质数,用hash与另一个大于散列表大小的质数做求余,这样得到的地址也能尽量不重复。 vardjb2HashCode=function(key){varhash=5381for(vari=0;i 不过,这几天自己不断地研究数据结构,也让自己对于JS的理解进一步加深了。继续努力吧~