本文主要介紹字典及散列表相關內容,在ES2015 中已經對字典進行了實現 既Map類
,本文中不會過多介紹Map ,學習的話能夠直接看阮一峯老師的ES6 Mapjavascript
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
class Dictionary {
constructor() {
this.table = {};
}
//將 key 轉化爲字 符串的函數
toStrFn(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString(); // {1}
}
hasKey(key) {
return this.table[this.toStrFn(key)] != null;
}
set(key, value) {
if (key != null && value != null) {
const tableKey = this.toStrFn(key); // {1}
this.table[tableKey] = new ValuePair(key, value);
return true;
}
return false;
}
remove(key) {
if (this.hasKey(key)) {
delete this.table[this.toStrFn(key)];
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.toStrFn(key)];
return valuePair == null ? undefined : valuePair.value; // {2}
}
keyValues() {
return Object.values(this.table);
}
keys() {
return this.keyValues().map(valuePair => valuePair.key);
}
values() {
return this.keyValues().map(valuePair => valuePair.value);
}
forEach(callbackFn) {
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i++) {
const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
if (result === false) {
break;
}
}
}
size() {
return Object.keys(this.table).length;
}
clear() {
this.table = {};
}
toString() {
if (this.isEmpty()) {
return '';
}
const valuePairs = this.keyValues();
let objString = `${valuePairs[0].toString()}`;
for (let i = 1; i < valuePairs.length; i++) {
objString = `${objString},${valuePairs[i].toString()}`;
}
return objString;
}
isEmpty() {
return this.size() === 0;
}
}
//使用
const dictionary = new Dictionary();
dictionary.set('Gandalf', 'gandalf@email.com');
dictionary.set('John', 'johnsnow@email.com');
dictionary.set('Tyrion', 'tyrion@email.com');
console.log(dictionary.hasKey('Gandalf')); //true
console.log(dictionary.size()); //3
console.log(dictionary.keys()); // ["Gandalf", "John", "Tyrion"]
console.log(dictionary.values()); //["gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"]
console.log(dictionary.get('Gandalf')) //gandalf@email.com
複製代碼
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
class HashTable {
constructor() {
this.table = {};
}
//將 key 轉化爲字 符串的函數
toStrFn(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString(); // {1}
}
// key 的 每一個字符 ASCII 碼之和
loseloseHashCode(key) {
if (typeof key === 'number') {
return key;
}
const tableKey = this.toStrFn(key);
let hash = 0;
for (let i = 0; i < tableKey.length; i++) {
hash += tableKey.charCodeAt(i); // {4}
}
return hash % 37; // {5}
}
hashCode(key) {
return this.loseloseHashCode(key);
}
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
this.table[position] = new ValuePair(key, value);
return true;
}
return false;
}
get(key) {
const valuePair = this.table[this.hashCode(key)];
return valuePair == null ? undefined : valuePair.value;
}
remove(key) {
const hash = this.hashCode(key);
const valuePair = this.table[hash];
if (valuePair != null) {
delete this.table[hash];
return true;
}
return false;
}
}
const hash = new HashTable();
hash.put('Gandalf', 'gandalf@email.com');
hash.put('John', 'johnsnow@email.com');
hash.put('Tyrion', 'tyrion@email.com');
console.log(hash.hashCode('Gandalf') + ' - Gandalf'); //19 - Gandalf
console.log(hash.hashCode('John') + ' - John'); //29 - John
console.log(hash.hashCode('Tyrion') + ' - Tyrion') //16 - Tyrion
複製代碼
有時候一些鍵會有相同的鍵值。不一樣的的值在散列表中對應相同位置的時候,咱們稱其爲衝突。此時,當咱們經過相同的散列值去取屬性值的時候會出現相互覆蓋、數據丟失的狀況。處理衝突有幾種方法:分離連接,線性探查和雙散列法,前端
class HashTableSeparateChaining {
constructor() {
this.table = {};
}
//將 key 轉化爲字 符串的函數
toStrFn(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString(); // {1}
}
// key 的 每一個字符 ASCII 碼之和
loseloseHashCode(key) {
if (typeof key === 'number') {
return key;
}
const tableKey = this.toStrFn(key);
let hash = 0;
for (let i = 0; i < tableKey.length; i++) {
hash += tableKey.charCodeAt(i); // {4}
}
return hash % 37; // {5}
}
hashCode(key) {
return this.loseloseHashCode(key);
}
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
if (this.table[position] == null) { //
this.table[position] = new SinglyLinkedList(); // 爲 鏈表章節中的類 https://juejin.im/post/5e363960f265da3e51531be6#heading-19
}
this.table[position].push(new ValuePair(key, value));
return true;
}
return false;
}
get(key) {
const position = this.hashCode(key);
const linkedList = this.table[position];
if (linkedList != null && !linkedList.isEmpty()) {
let current = linkedList.getHead();
while (current != null) {
if (current.element.key === key) {
return current.element.value;
}
current = current.next;
}
}
return undefined;
}
remove(key) {
const position = this.hashCode(key);
const linkedList = this.table[position];
if (linkedList != null && !linkedList.isEmpty()) {
let current = linkedList.getHead();
while (current != null) {
if (current.element.key === key) {
linkedList.remove(current.element);
if (linkedList.isEmpty()) {
delete this.table[position];
}
return true;
}
current = current.next;
}
}
return false;
}
}
複製代碼
當想向表中某個位置加入一個新元素的時候,若是索引爲index的位置已經被佔據了,就嘗試index+1的位置。若是index+1的位置也被佔據了,就嘗試index+2的位置,以此類推。示例代碼以下java
class HashTableSeparateChaining {
constructor() {
this.table = {};
}
//將 key 轉化爲字 符串的函數
toStrFn(item) {
if (item === null) {
return 'NULL';
} else if (item === undefined) {
return 'UNDEFINED';
} else if (typeof item === 'string' || item instanceof String) {
return `${item}`;
}
return item.toString(); // {1}
}
// key 的 每一個字符 ASCII 碼之和
loseloseHashCode(key) {
if (typeof key === 'number') {
return key;
}
const tableKey = this.toStrFn(key);
let hash = 0;
for (let i = 0; i < tableKey.length; i++) {
hash += tableKey.charCodeAt(i); // {4}
}
return hash % 37; // {5}
}
hashCode(key) {
return this.loseloseHashCode(key);
}
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
if (this.table[position] == null) {
this.table[position] = new ValuePair(key, value);
} else {
let index = position + 1;
while (this.table[index] != null) {
index++;
}
this.table[index] = new ValuePair(key, value);
}
return true;
}
return false;
}
get(key) {
const position = this.hashCode(key);
if (this.table[position] != null) {
if (this.table[position].key === key) {
return this.table[position].value;
}
let index = position + 1;
while (this.table[index] != null && this.table[index].key !== key) { // {5}
index++;
}
if (this.table[index] != null && this.table[index].key === key) { // {6}
return this.table[position].value;
}
}
return undefined;
}
remove(key) {
const position = this.hashCode(key);
if (this.table[position] != null) {
if (this.table[position].key === key) {
delete this.table[position]; // {1}
this.verifyRemoveSideEffect(key, position);
return true;
}
let index = position + 1;
while (this.table[index] != null && this.table[index].key !== key ) {
index++;
}
if (this.table[index] != null && this.table[index].key === key) {
delete this.table[index]; // {3}
this.verifyRemoveSideEffect(key, index); // {4}
return true;
}
}
return false;
}
//反作用驗證
verifyRemoveSideEffect(key, removedPosition) {
const hash = this.hashCode(key); // {1}
let index = removedPosition + 1; // {2}
while (this.table[index] != null) { // {3}
const posHash = this.hashCode(this.table[index].key); // {4}
if (posHash <= hash || posHash <= removedPosition) { // {5}
this.table[removedPosition] = this.table[index]; // {6}
delete this.table[index];
removedPosition = index;
}
index++;
}
}
}
複製代碼
前端界的一枚小學生es6