本文是博主深感算法方面的不足,做的一系列讀書筆記和源碼分析。
原文地址:學習javascript數據結構(四)——字典和散列表,以爲有用的話能夠給個star,謝謝啦。
做者:wengjqjavascript
字典存儲的是[鍵,值]對,其中鍵名是用來查詢特定元素的。字典和集合很類似,集合以[值,值]的形式存儲元素,字典則是以[鍵,值]的形式來存儲元素。字典也稱映射。示例代碼以下:java
function Dictionary(){
var items = {};
this.set = function(key, value){
items[key] = value;
};
this.remove = function(key){
if (this.has(key)){
delete items[key];
return true;
}
return false;
};
this.has = function(key){
return items.hasOwnProperty(key);
};
this.get = function(key) {
return this.has(key) ? items[key] : undefined;
};
this.clear = function(){
items = {};
};
this.size = function(){
return Object.keys(items).length;
};
this.keys = function(){
return Object.keys(items);
};
this.values = function(){
var values = [];
for (var k in items) {
if (this.has(k)) {
values.push(items[k]);
}
}
return values;
};
this.each = function(fn) {
for (var k in items) {
if (this.has(k)) {
fn(k, items[k]);
}
}
};
this.getItems = function(){
return items;
}
}複製代碼
散列表即HashTable類,也叫HashMap類,是Dictionary類的一種散列實現方式。散列算法的做用是儘量的在數據結構中找到一個值。在之前的系列中,若是要在數據結構中獲取一個值,須要遍歷整個數據結構來找到它。若是使用散列函數,就知道值的具體位置,所以可以快速檢索到該值。散列函數的做用是給定一個鍵值,而後返回值在表中的位置。示例以下:git
function HashTable() {
var table = [];
var loseloseHashCode = function (key) { //(1)散列函數
var hash = 0;
for (var i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
};
var djb2HashCode = function (key) { //(2)散列函數
var hash = 5381;
for (var i = 0; i < key.length; i++) {
hash = hash * 33 + key.charCodeAt(i);
}
return hash % 1013;
};
var hashCode = function (key) {
return loseloseHashCode(key);
};
this.put = function (key, value) { //根據所給的key經過散列函數計算出它在表中的位置,進而做映射
var position = hashCode(key);
console.log(position + ' - ' + key);
table[position] = value;
};
this.get = function (key) {
return table[hashCode(key)];
};
this.remove = function(key){
table[hashCode(key)] = undefined;
};
this.print = function () {
for (var i = 0; i < table.length; ++i) {
if (table[i] !== undefined) {
console.log(i + ": " + table[i]);
}
}
};
}複製代碼
有時候一些鍵會有相同的鍵值。不一樣的的值在散列表中對應相同位置的時候,咱們稱其爲衝突。此時,當咱們經過相同的散列值去取屬性值的時候會出現相互覆蓋、數據丟失的狀況。處理衝突有幾種方法:分離連接,線性探查和雙散列法,這裏介紹前兩種。github
分離連接法包括爲散列表的每一個位置建立一個鏈表並將元素存儲在裏面。示例代碼以下:算法
function HashTableSeparateChaining(){
var table = [];
var ValuePair = function(key, value){ //新的輔助類來加入LinkedList實例的元素,用到以前的鏈表
this.key = key;
this.value = value;
this.toString = function() {
return '[' + this.key + ' - ' + this.value + ']';
}
};
var loseloseHashCode = function (key) { //散列函數得出一個散列值key
var hash = 0;
for (var i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
};
var hashCode = function(key){
return loseloseHashCode(key);
};
this.put = function(key, value){
var position = hashCode(key);
console.log(position + ' - ' + key);
if (table[position] == undefined) { //判斷是否被佔據了
table[position] = new LinkedList();
}
table[position].append(new ValuePair(key, value)); //LinkedList實例中添加一個ValuePair實例
};
this.get = function(key) {
var position = hashCode(key);
if (table[position] !== undefined && !table[position].isEmpty()){
var current = table[position].getHead();
while(current.next){ //遍歷鏈表來尋找鍵值
if (current.element.key === key){
return current.element.value;
}
current = current.next;
}
//檢查元素在鏈表第一個或最後一個節點的狀況
if (current.element.key === key){
return current.element.value;
}
}
return undefined;
};
this.remove = function(key){
var position = hashCode(key);
if (table[position] !== undefined){
var current = table[position].getHead();
while(current.next){ //遍歷
if (current.element.key === key){
table[position].remove(current.element);
if (table[position].isEmpty()){
table[position] = undefined;
}
return true;
}
current = current.next;
}
//檢查元素在鏈表第一個或最後一個節點的狀況
if (current.element.key === key){
table[position].remove(current.element);
if (table[position].isEmpty()){
table[position] = undefined;
}
return true;
}
}
return false;
};
this.print = function() {
for (var i = 0; i < table.length; ++i) {
if (table[i] !== undefined) {
console.log(table[i].toString());
}
}
};
}複製代碼
當想向表中某個位置加入一個新元素的時候,若是索引爲index的位置已經被佔據了,就嘗試index+1的位置。若是index+1的位置也被佔據了,就嘗試index+2的位置,以此類推。示例代碼以下:數據結構
function HashLinearProbing(){
var table = [];
var ValuePair = function(key, value){
this.key = key;
this.value = value;
this.toString = function() {
return '[' + this.key + ' - ' + this.value + ']';
}
};
var loseloseHashCode = function (key) {
var hash = 0;
for (var i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % 37;
};
var hashCode = function(key){
return loseloseHashCode(key);
};
this.put = function(key, value){
var position = hashCode(key);
console.log(position + ' - ' + key);
if (table[position] == undefined) { //若是沒有元素存在加入
table[position] = new ValuePair(key, value);
} else {
var index = ++position;
while (table[index] != undefined){ //有的話繼續日後找,直到找到加入
index++;
}
table[index] = new ValuePair(key, value);
}
};
this.get = function(key) {
var position = hashCode(key);
if (table[position] !== undefined){
if (table[position].key === key) {
return table[position].value;
} else {
var index = ++position;
while (table[index] === undefined || table[index].key !== key){ //循環迭代
index++;
}
if (table[index].key === key) { //驗證key
return table[index].value;
}
}
}
return undefined;
};
this.remove = function(key){
var position = hashCode(key);
if (table[position] !== undefined){
if (table[position].key === key) {
table[position] = undefined;
} else {
var index = ++position;
while (table[index] === undefined || table[index].key !== key){
index++;
}
if (table[index].key === key) {
table[index] = undefined;
}
}
}
};
this.print = function() {
for (var i = 0; i < table.length; ++i) {
if (table[i] !== undefined) {
console.log(i + ' -> ' + table[i].toString());
}
}
};
}複製代碼