JavaScript - 字典和散列表
大约 3 分钟
JavaScript - 字典和散列表
字典和散列表
export function defaultToString(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(); // 需要实现 toString 方法
}
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
export default class Dictionary {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
// 检测一个键是否存在于字典
hasKey(key) {
return this.table[this.toStrFn(key)] != null;
}
// 设置键值
set(key, value) {
if (key != null && value != null) {
const tableKey = this.toStrFn(key);
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;
}
// keys、values 和 valuePairs 方法
keyValues() {
// return Object.values(this.table);
const valuePairs = [];
for (const k in this.table) {
if (this.hasKey(k)) {
valuePairs.push(this.table[k]);
}
}
return valuePairs;
}
keys() {
// return this.keyValues().map(valuePair => valuePair.key);
const keys = [];
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i += 1) {
keys.push(valuePairs[i].key);
}
return keys;
}
// forEach 迭代字典
forEach(callbackFn) {
const valuePairs = this.keyValues();
for (let i = 0; i < valuePairs.length; i += 1) {
const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
if (result === false) {
break;
}
}
}
// clear、size、isEmpty 和 toString 方法
size() {
return this.keyValues().length;
}
isEmpty() {
return this.size() === 0;
}
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 += 1) {
objString += `,${valuePairs[i].toString()}`;
}
return objString;
}
}
散列表
export function defaultToString(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(); // 需要实现 toString 方法
}
class ValuePair {
constructor(key, value) {
this.key = key;
this.value = value;
}
toString() {
return `[#${this.key}: ${this.value}]`;
}
}
class HashTable {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
// 创建散列函数
loseloseHashCode(key) {
if (typeof key === "number") {
return key;
}
const tableKey = this.toStrFn(key);
let hash = 0;
for (let i = 0; i < tableKey.length; i += 1) {
hash += tableKey.charCodeAt(i);
}
return hash % 37;
}
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;
}
toString() {
if (this.isEmpty()) {
return "";
}
const keys = Object.keys(this.table);
let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`;
for (let i = 1; i < keys.length; i += 1) {
objString += `{${keys[i]} => ${this.table[keys[i]].toString()}}`;
}
return objString;
}
}
分离链接
通过链表存储元素,解决冲突,LinkedList 参考链表
class HashTableSeparateChaining {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
// put 方法
put(key, value) {
if (key != null && value != null) {
const position = this.hashCode(key);
if (this.table[position] == null) {
this.table[position] = new LinkedList(); // LinkedList
}
this.table[position].push(new ValuePair(key, value));
return true;
}
return false;
}
// get 方法
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 方法
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;
}
}
线性探查
将元素直接存储到表中
class HashTableLinearProbing {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn;
this.table = {};
}
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 += 1;
}
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) {
index += 1;
}
if (this.table[index] != null && this.table[index].key === key) {
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];
this.verifyRemoveSideEffect(key, position);
return true;
}
let index = position + 1;
while (this.table[index] != null && this.table[index].key !== key) {
index += 1;
}
if (this.table[index] != null && this.table[index].key === key) {
delete this.table[index];
this.verifyRemoveSideEffect(key, index);
return true;
}
}
return false;
}
verifyRemoveSideEffect(key, removedPostion) {
const hash = this.hashCode(key);
let index = removedPosition + 1;
while(this.table[index] != null) {
cost posHash = this.hashCode(this.table[index].key);
if(posHash <= hash || posHash <= removedPosition) {
this.table[removedPosition] = this.table[index];
delete this.table[index];
removedPosition = index;
}
index += 1
}
}
}