散列表的实现 -- 数据结构与算法的javascript描述 第八章
2014-09-29 17:29
615 查看
散列表(哈希表
散列是一种常用的数据存储技术,散列后的数据可以快速地插入或取用。
散列表需要一个散列值(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] +" \n"; } } 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] +" \n"; } } console.log(s) } function showPutLength(){ console.log("共有 "+me.putLength+" 次插入散列表行为"); return me.putLength; } }
散列表总结
实现一个散列表存储最主要的问题就是解决 碰撞问题。
碰撞原因:
我们要将一个字符或者整型键进行存储,需要对其进行计算散列值(计算方式为每个字符的ASCII码值相加,尽管计算方法升级优化了 仍会有碰撞的可能),但是不同的字符或者整形可能计算出同一个散列值,这样就会发生碰撞,后面插入的值会覆盖前面的值。
比如 “Clayton”与 “Raymond” 计算后的散列值一样。
为了尽量避免碰撞,我们对计算散列值的方法进行了优化,新的散列函数仍然先计算字符串中各字符的ASCII码值,不过求和时每次要乘以一个质数。具体可以搜索:[霍纳算法],注: 这一版本 虽然优化了计算方法,仍然有碰撞的可能
碰撞处理:
开链法:
是指实现散列表的底层数组中,每个数组元素又是一个新的数据结构,比如另一个数组,这样就能存储多个键了。使用这种技术,即使两个键散列后的值相同,依然被保存在同样的位置,只不过它们在第二个数组中的位置不一样罢了
开链法的可能存储结构:
[ [1], [2], [3,3,3,3] //多个数据 散列值一样,但是存储在同一位置不同下标中。 ]
实现开链法的方法是:在创建存储散列过的键值的数组时,通过调用一个函数创建一个新的空数组,然后将该数组赋给散列表里的每个数组元素。这样就创建了一个二维数组
线性探测法:
当发生碰撞时,线性探测法检查散列表中的下一个位置是否为空。如果为空,就将数据存入该位置;如果不为空,则继续检查下一个位置,直到找到一个空的位置为止。该技术是基于这样一个事实:每个散列表都会有很多空的单元格,可以使用它们来存储数据
到此为止,只是了解了如何实现不碰撞的方法,但碍于水平有限,并未实现完整的代码,所以就不贴出来了。希望能对看到的人有所帮助~。
相关文章推荐
- JavaScript描述数据结构与算法——队列
- Javascript数据结构与算法--队列(顺序队列、优先队列、循环队列)的实现与用法
- JavaScript描述数据结构与算法——列表
- Javascript数据结构与算法--栈的实现与用法
- javascript数据结构与算法--散列
- 数据结构与算法——散列表类的C++实现(探测散列表)
- [置顶] 【javascript 数据结构与算法】javascript实现冒泡排序
- 数据结构中关键路径算法的实现与应用
- 数据结构与算法(C#实现)系列---二叉树
- 转:C#数据结构和算法学习系列十二----散列HashTable类
- 无限级菜单结构 与 Javascript 实现算法, 类 XMenu by shawl.qiu
- 数据结构与算法---C#实现LinkedList实例
- 【数据结构与算法】第三章 栈c实现,使用链表结构
- 在SQL Server中巧妙存储树形结构数据及ASP.Net javaScript无刷新树形GridView的实现
- 【数据结构与算法】第三章 表c实现
- 数据结构与算法(C#实现)系列---二叉树
- 算法:C语言实现 (第1-4部分)基础知识、数据结构……
- JavaScript描述数据结构之线性表(顺序存储)
- 数据结构——快速排序原理及算法Java实现
- 数据结构与算法之单链表 自己实现STL list模板类