js算法入门(2)--哈希表

发布时间:2019-08-06 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了js算法入门(2)--哈希表脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

1.简介

@H_126_3@哈希表(hash table)又被称为散列表,可能是翻译的问题好多书上一会儿称散列一会儿称哈希,更有甚者煞有介事的对此进行区分。经过简单的搜索(wiki链接)发现这两个词是一回事。由此可见学好英语是多么重要。(我是一名渴望学好英语的英文渣。。)

1.1定义

这里引用一下维基百科的定义:

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数(Hash function),将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。

这段话里加粗的地方暂时存疑,因为和下一句话说法有冲突,感觉改为根据哈希函数与键计算出来的值,即哈希值访问内存存储位置的数据结构会好点(当然也有可能是我阅读理解不好,哈哈)

1.2一些点

哈希表是

@H_406_19@
  • 一种动态(指数据存入后,还会进行增删查改等工作)集合结构
  • 至少需要支持insert,seArch,delete等操作
  • 普通数组推广概念

    • 数组是直接寻址
    • 当实际存储的key数量小于key的总数量,使用哈希表是直接数组寻址的有效替代
  • 不是直接把key作为数组下标,而是根据哈希函数与key计算出相应的下标
  • 2直接寻址表(direct-address table)

    2.1废话

    资料找多了会发现一个很严重的问题,它们之间可能会有冲突,从简介中介绍的名字问题就可以看出。同样,有些书把直接寻址表也视作一种哈希表,不过哈希表既然是数组的一种推广,那也就不要在这些细枝末节上计较了。

    2.2介绍

    当关键词的数量比较小时,这种方法是一种简单有效的方法,在我的文章《如何随机&&去重返回新数组》中3.1节给出的方法就用到了直接寻址处理问题,代码中数组indexArr就是。这是一种空间换时间的做法,定义一个大于等于key数量的数组,value部分全部初始化为null,然后进行数据的存取。这也是我们经常使用的。

    3哈希表(hash table)

    直接寻址的缺点在于如果数据量很大,占用空间就很大,因为首先你得初始化一个巨大的数组,无论数据是否存入。如果用一句话总结哈希表就是:hash浓缩为一句话:将元素通过一个函数转换为整数,使得该整数可以尽量唯一的表达这个元素

    3.1js中数组和对象与哈希表的关系

    但是在js中其实这个问题有待于商榷,因为js的数组还有对象都可以存任意键值而且无需提前定义长度,还可以随意增删。所以有的文章就指出其实js的数组和对象就的底层实现就是哈希表(文章地址),虽然文章中只是提到对象,但是基于js存在key-value形式的数组,我猜原理应该很是类似。

    3.2基础的哈希函数

    设哈希函数为H,数据的键设为key,转换后的值为整数H(key)。常见的有平方取中发,除留余数法,线性变化法(H(key)=a*key+b)。这里着重介绍除留余数法。

    3.2.1除留余数法

    公式:

    H(key)=key%mod

    把key除以一个数mod得到的余数作为hash值的方法,通过这个哈希函数可以把很大的数转换为不超过mod的整数,这表示数组的长度必须不小于mod(js中无所谓),当mod是一个素数时H(key)能尽可能的覆盖[0,mod)范围内的每一个数。

    3.3冲突

    看3.2.1的公式就可以知道,必然会出现两个不同的key1,key2他们的hash的值H(key1)=H(key2)。如果直接把hash值作为数组下表标则会出现覆盖的情况,我们称之为冲突,由此看出hash函数不是单射,这样也就表明hash值是不可逆的。

    3.3.1常见的解决冲突的方法

    1. 线性探查法
    2. 平方探查法
    3. 链地址法

    1,2都要重新计算hash值,3不需要,而且3是c语言里常见的解决方法,思想是把所有H(key)相同的key连成一条单链表(当然用一个数组也是可以的),然后查找时遍历单链表寻找数据。这些都是底层,大部分语言都封装有库。

    3.4字符串hash初步

    字符串hash是指将一个字符串S映射为一个整数,使得该整数可以尽可能唯一地代表字符串S。为什么要这么做呢,因为好多语言的数组的下标只能接受整数,例如C语言这种静态的语言和js这种动态语言数据存储上差异很大。js中使用对象存储key-value形式的数据增删查改都非常方便,但是在C语言中需要很多数据结构配合的使用才能实现。

      function hashFunc(s='hello word'){
            let id=0,len,arr=[];
            len=s.length;
            arr=s.split("");
            for(let i=0;i<len;i++){
                id += arr[i].charCodeAt();//str.charCodeAt(index)用于获取字符的ascii码
            }
            return id%57;//找一个素数用来限制数组大小
        }

    js实现这个函数还是很方便的,就是字符ascii码相加即可。当然还可以使用更复杂的方式来避免冲突。

    3.5js实现哈希表

    我们将要实现hashTable这个类分别实现插入、查找、删除、打印等方法。

        class HashTable {
            constructor() {
                this.table = {'3212':{'ddd':'ddd','ee':'2312'}};//测试递归是否正常
            }
    
            _hashFunc(key) {
                let id = 0, len, arr = [];
                len = key.length;
                arr = key.split("");
                for (let i = 0; i < len; i++) {
                    id += arr[i].charCodeAt();//str.charCodeAt(index)用于获取字符的ascii码
                }
                return id%57;//找一个素数用来限制数组大小
            }
            insert(key,value){
                if(typeof key !='object'){//可以只接受一个对象
                    let id = this._hashFunc(key);
                    if(!this.table[id]){
                        this.table[id]={};
                    }
                    if(!this.table[id][key]){
                        this.table[id][key]=value;
                    }
                }else{
                    for(let i in key){
                        this.insert(i,key[i])
                    }
                }
    
            }
            search(key){
                let id = this._hashFunc(key);
                if(!this.table[id] || !this.table[id][key]) return null;
                return this.table[id][key];
            }
            delete(key){
                let id = this._hashFunc(key);
                if(this.table[id])
                    if(this.table[id][key])
                       return delete this.table[id][key]
            }
            PRint(table=this.table){//递归输出hashtable的值
                if(typeof table=='object'){
                    for(let key in table){
                        this.print(table[key])
                        if(typeof table[key]!='object')
                        console.LOG(key,'+',table[key])
                    }
                }
    
            }
        }
        let hash = new HashTable()
        hash.insert({'abc':'ddd@QQ.COM','bac':'33@qq.com','ddic':'2343@gmail.com'});
        hash.print();
        console.warn('delete abc')
        hash.delete('abc');
        hash.print();
        console.log(hash.search('bac'))

    这个代码里用了对象嵌套对象的形式实现了链地址法处理冲突在C语言中会选择数组+链表的形式实现,当后面写到链表的时候会重新改一下。其实就像上文中所述,js中的对象可能就是封装一个哈希表,而且key值是唯一的,连哈希函数貌似都可以省了了。

    参考书目

    《算法笔记》
    《算法导论》
    《学习JavaScript数据结构与算法》
    《ECMAScript 6 入门》

    脚本宝典总结

    以上是脚本宝典为你收集整理的js算法入门(2)--哈希表全部内容,希望文章能够帮你解决js算法入门(2)--哈希表所遇到的问题。

    如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

    本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
    如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。