zoukankan      html  css  js  c++  java
  • 散列表的实现 -- 数据结构与算法的javascript描述 第八章

    散列表(哈希表

    散列是一种常用的数据存储技术,散列后的数据可以快速地插入或取用。

    散列表需要一个散列值(key)来存储指定数据,取数据也是依靠此。

    散列值可以依靠计算数据的 ASCII码来获得,但是这会有一个问题,若干数据的散列值可能会相同,这样存储就会发生碰撞。

    方案:

    开链法, 对Hash表中每个Hash值建立一个冲突表,即将冲突的几个记录以表的形式存储在其中

    开放寻址散列,当发生碰撞时,线性探测法检查散列表中的下一个位置是否为空。如果为空,就将数据存入该位置;如果不为空,则继续检查下一个位置,直到找到一个空的位置为止。

    第一版code

     //版本一 (字符串类型的键
        //因其对散列值处理中 有碰撞的可能。
        //不具备处理碰撞数据的能力(有可能散列值相同,会把上一个值覆盖掉)
    
        function HashTable(){
            var me = this;
            me.table = new Array(137);
            me.simpleHash = simpleHash;
            me.showDistro = showDistro;
            me.showPutLength = showPutLength;
            //统计有多少数据进入散列表
            me.putLength = 0;
            me.put = put;
    
    
            /**
             * 取散列值  *取每个字符的ASCII码值之和
             * @param s
             * @returns {number}
             */
            function simpleHash(s){
                var total = 0;
                for (var i = 0; i < s.length; ++i) {
                    total += s.charCodeAt(i);
                }
                return total % me.table.length;
            }
            function put(data){
                me.putLength++;
                var pos = me.simpleHash(data);
                me.table[pos] = data;
            }
            function showDistro(){
                var s = '',i=0;
                for(;i<me.table.length;i++){
                    if (me.table[i] != undefined) {
                        s+=" key: "+ i+" , value:"+me.table[i] +" 
    ";
                    }
                }
                console.log(s)
            }
    
            function showPutLength(){
                console.log("共有 "+me.putLength+" 次插入散列表行为");
                return me.putLength;
            }
        }
    

    第一版测试

        //版本一测试
        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.showPutLength();
        hTable.showDistro();
    

    版本一的数据碰撞处理有问题。我们修改一个版本来优化这个地方。

    新的散列值计算方式用了 霍纳算法的思想,乘以一个质数来尽量避免碰撞。

    function HashTable2(){
            var me = this;
            me.table = new Array(137);
            me.simpleHash = simpleHash;
            me.showDistro = showDistro;
            me.showPutLength = showPutLength;
            me.put = put;
            //统计有多少数据进入散列表
            me.putLength = 0;
            //质数,当该数字式37时候 也会碰撞 +_+
            me.H = 31;
     
            //第二版 修正碰撞问题(只能说 尽可能减少碰撞  霍纳算法
            //为了避免碰撞,首先要确保散列表中用来存储数据的数组其大小是个质数
            //新的散列函数仍然先计算字符串中各字符的ASCII码值,不过求和时每次要乘以一个质数。
            /**
             * 取散列值  *取每个字符的ASCII码值之和
             * @param s
             * @returns {number}
             */
            function simpleHash(s){
                var total = 0;
                for (var i = 0; i < s.length; ++i) {
                    total += me.H * total + s.charCodeAt(i);
                }
                total = total % me.table.length;
                if (total < 0) {
                    total += me.table.length-1;
                }
                return parseInt(total);
            }
            function put(data){
                me.putLength++;
                var pos = me.simpleHash(data);
                me.table[pos] = data;
            }
            function showDistro(){
                var s = '',i=0;
                for(;i<me.table.length;i++){
                    if (me.table[i] != undefined) {
                        s+=" key: "+ i+" , value: "+me.table[i] +" 
    ";
                    }
                }
                console.log(s)
            }
            function showPutLength(){
                console.log("共有 "+me.putLength+" 次插入散列表行为");
                return me.putLength;
            }
        }
    

    散列表总结

    实现一个散列表存储最主要的问题就是解决 碰撞问题。

    碰撞原因:
    我们要将一个字符或者整型键进行存储,需要对其进行计算散列值(计算方式为每个字符的ASCII码值相加,尽管计算方法升级优化了 仍会有碰撞的可能),但是不同的字符或者整形可能计算出同一个散列值,这样就会发生碰撞,后面插入的值会覆盖前面的值。

    比如 “Clayton”与 “Raymond” 计算后的散列值一样。

    为了尽量避免碰撞,我们对计算散列值的方法进行了优化,新的散列函数仍然先计算字符串中各字符的ASCII码值,不过求和时每次要乘以一个质数。具体可以搜索:[霍纳算法],注: 这一版本 虽然优化了计算方法,仍然有碰撞的可能

    碰撞处理:

    开链法:

    是指实现散列表的底层数组中,每个数组元素又是一个新的数据结构,比如另一个数组,这样就能存储多个键了。使用这种技术,即使两个键散列后的值相同,依然被保存在同样的位置,只不过它们在第二个数组中的位置不一样罢了

    开链法的可能存储结构:

          [  [1],
             [2],
             [3,3,3,3]  //多个数据 散列值一样,但是存储在同一位置不同下标中。
          ]
    

    实现开链法的方法是:在创建存储散列过的键值的数组时,通过调用一个函数创建一个新的空数组,然后将该数组赋给散列表里的每个数组元素。这样就创建了一个二维数组

    线性探测法:

    当发生碰撞时,线性探测法检查散列表中的下一个位置是否为空。如果为空,就将数据存入该位置;如果不为空,则继续检查下一个位置,直到找到一个空的位置为止。该技术是基于这样一个事实:每个散列表都会有很多空的单元格,可以使用它们来存储数据

    到此为止,只是了解了如何实现不碰撞的方法,但碍于水平有限,并未实现完整的代码,所以就不贴出来了。希望能对看到的人有所帮助~。
  • 相关阅读:
    redis+nginx+tomcat故障大全
    Nginx+Tomcat+Redis实现负载均衡、资源分离、session共享
    Redis初步认识
    私有IP地址共有三个范围段
    公有云和私有云的区别
    svn安装与其服务器搭建
    datagrid后台分页js
    easyui.dialog.js
    EasyUI 另一种form提交方式
    easyui- grid前台分页
  • 原文地址:https://www.cnblogs.com/iyueyao/p/4000688.html
Copyright © 2011-2022 走看看