/* * 算法——散列 * 散列表是基于数组进行设计的。数组的长度是预先设定的 * 所有元素根据和该元素对应的键,保存在数组的特定位置,该键和我们前面讲到的字典中的键是类似的概念 * 使用散列表存储数据时,通过一个散列函数将键映射为一个数字,这个数字的范围是 0 到散列表的长度。 * * 这里所说的键 应该就是数组的索引 只不过 数组既然长度确定了,那么索引的个数也是确定了 * 每个元素是无限的,每个元素的键值映射出来的索引也许就会发生重复或说碰撞。 这个叫有效碰撞。 * * 键值映射到数组的索引 这个过程是由散列函数来完成。 散列函数的选择 要保证键值映射出来的索引是尽量不重复的 * * simpleHash: 散列函数 * * put: 在散列中增加元素 * * showDistro: 展示散列中的元素 * */ function simpleHash(data) { var total = 0; for(var i = 0 ; i < data.length; i++) { total += data.charCodeAt(i); } return total % this.table.length; } function put(data) { var pos = this.simpleHash(data); this.table[pos] = data; } function showDistro() { for(var i = 0 ; i < this.table.length;i++) { if (this.table[i] !== undefined) { console.log(i + ":" +this.table[i]) } } } function HashTable() { this.table = new Array(137); this.buildChains = buildChains; this.simpleHash = simpleHash; this.put = put; this.showDistro = showDistro; } var someNames = ["David", "Jennifer", "Donnie", "Raymond", "Cynthia", "Mike", "Clayton", "Danny", "Jonathan"], h = new HashTable(); // h.buildChains(); //测试线性探测法 注释掉 for (var i = 0; i< someNames.length;i++) { h.put(someNames[i]); } h.showDistro(); // 输出:35: Cynthia 45: Clayton 57: Donnie 77: David 95: Danny 116: Mike 132: Jennifer 134: Jonathan //是输出了8位少了一位? 记得上面的说明 散列函数可能会映射相同的索引出来 出现了有效碰撞。 //更好的散列函数 数组长度是质数 计算过程中再乘以一个质数 //为什么要选择一个质数呢? 质数特点:只有1 和 本身两个因数 而合数 有两个以上因数 如果除余合数 那么造成重复机会大 //因为合数有多个因子 比如 20 50%20 70%20 是一样的 //感觉这个质数不是特别靠谱 ^^ 下面有其它方法解决 function simpleHash(data) { const H = 5; var total = 0; for (var i = 0; i < data.length; ++i) { total += H * total + data.charCodeAt(i); } return total % this.table.length; } /*碰撞处理 * 1、开链法:开链法是指实现散列表的底层数组中,每个数组元素又是一个新的数据结构,比如另一个数组,这样就能存储多个键了 * 2、线性探测法 线性探测法检查散列表中的下一个位置是否为空。如果为空,就将数据存入该位置;如果不为空,则继续检查下一个位置,直到找到一个空的位置为止 * */ /* * 两个方法该如何选择? * 当存储数据使用的数组特别大时,选择线性探测法要比开链法好。这里有一个公式,常常 可以帮助我们选择使用哪种碰撞解决办法:如果数组的大小是待存储数据个数的 1.5 倍, 那么使用开链法;如果数组的大小是待存储数据的两倍及两倍以上时,那么使用线性探 测法。 * */ /*下面这些方法是对上面的方法重新或新增*/ /*开链法*/ function buildChains() { for(var i = 0; i < this.table.length;i++) { this.table[i] = new Array(); } } // function showDistro() { // for(var i = 0 ; i < this.table.length;i++) { // if (this.table[i][0] !== undefined) { // console.log(i + ":" +this.table[i]) // } // } // } // function put(data) { // var pos = this.simpleHash(data); // this.table[pos].push(data); // } /*线性探测法*/ function put(data) { var pos = this.simpleHash(data); while (this.table[pos] !== undefined) { pos++; } this.table[pos] = data; }