资讯专栏INFORMATION COLUMN

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

Lavender / 2461人阅读

摘要:简介哈希表又被称为散列表,可能是翻译的问题好多书上一会儿称散列一会儿称哈希,更有甚者煞有介事的对此进行区分。实现哈希表我们将要实现这个类分别实现插入查找删除打印等方法。

1.简介

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

1.1定义

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

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

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

1.2一些点

哈希表是

一种动态(指数据存入后,还会进行增删查改等工作)集合结构

至少需要支持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都要重新计算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

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

3.5js实现哈希表

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

    class HashTable {
        constructor() {
            this.table = {"3212":{"ffffd":"ffffd","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":"ffffd@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 入门》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94255.html

相关文章

  • 唠叨一下js对象与哈希那些事

    摘要:的扩展知识对于哈希表来说,最重要的莫过于生成哈希串的哈希算法和处理冲突的策略了。由于链表的查找需要遍历,如果我们将链表换成树或者哈希表结构,那么就能大幅提高冲突元素的查找效率。 最近在整理数据结构和算法相关的知识,小茄专门在github上开了个repo https://github.com/qieguo2016...,后续内容也会更新到这里,欢迎围观加星星! js对象 js中的对象是基...

    Nosee 评论0 收藏0
  • JS数据结构与算法_集合&字典

    摘要:上一篇数据结构与算法链表写在前面说明数据结构与算法系列文章的代码和示例均可在此找到一集合集合数据结构集合是一种包含不同元素的数据结构。集合中的元素成为成员。 上一篇:JS数据结构与算法_链表 写在前面 说明:JS数据结构与算法 系列文章的代码和示例均可在此找到 一、集合Set 1.1 集合数据结构 集合set是一种包含不同元素的数据结构。集合中的元素成为成员。集合的两个最重要特性是:...

    sf_wangchong 评论0 收藏0
  • js数据结构和算法(五)字典和散列(hash)

    摘要:哈希表也是种数据结构,它可以提供快速的插入操作和查找操作。一个更好的散列函数为了避免碰撞,首先要确保散列表中用来存储数据的数组其大小是个质数,这和计算散列值时使用的取余运算有关。散列函数将学生里的数字相加,使用函数计算出散列值。 什么是字典结构? 字典是以键值对形式存储数据的数据结构,就像电话号码薄里的名字和电话号码那样的一一对应的关系。 javascript的Object类就是以...

    Hegel_Gu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<