Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(chapter_hashing): Add js and ts codes for chapter hashing #675

Merged
merged 5 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions codes/javascript/chapter_hashing/array_hash_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ArrayHashMap {
let arr = [];
for (let i = 0; i < this.#buckets.length; i++) {
if (this.#buckets[i]) {
arr.push(this.#buckets[i]?.key);
arr.push(this.#buckets[i].key);
}
}
return arr;
Expand All @@ -73,7 +73,7 @@ class ArrayHashMap {
let arr = [];
for (let i = 0; i < this.#buckets.length; i++) {
if (this.#buckets[i]) {
arr.push(this.#buckets[i]?.val);
arr.push(this.#buckets[i].val);
}
}
return arr;
Expand Down
144 changes: 144 additions & 0 deletions codes/javascript/chapter_hashing/hash_map_chaining.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* File: hash_map_chaining.js
* Created Time: 2023-08-06
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 键值对 Number -> String */
class Pair {
constructor(key, val) {
this.key = key;
this.val = val;
}
}

/* 链式地址哈希表 */
class HashMapChaining {
#size; // 键值对数量
#capacity; // 哈希表容量
#loadThres; // 触发扩容的负载因子阈值
#extendRatio; // 扩容倍数
#buckets; // 桶数组

/* 构造方法 */
constructor() {
this.#size = 0;
this.#capacity = 4;
this.#loadThres = 2 / 3.0;
this.#extendRatio = 2;
this.#buckets = new Array(this.#capacity).fill(null).map((x) => []);
}

/* 哈希函数 */
#hashFunc(key) {
return key % this.#capacity;
}

/* 负载因子 */
#loadFactor() {
return this.#size / this.#capacity;
}

/* 查询操作 */
get(key) {
const index = this.#hashFunc(key);
const bucket = this.#buckets[index];
// 遍历桶,若找到 key 则返回对应 val
for (let pair of bucket) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let pair of bucket) {
for (const pair of bucket) {

if (pair.key === key) {
return pair.val;
}
}
// 若未找到 key 则返回 null
return null;
}

/* 添加操作 */
put(key, val) {
// 当负载因子超过阈值时,执行扩容
if (this.#loadFactor() > this.#loadThres) {
this.#extend();
}
const index = this.#hashFunc(key);
const bucket = this.#buckets[index];
// 遍历桶,若遇到指定 key ,则更新对应 val 并返回
for (let pair of bucket) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let pair of bucket) {
for (const pair of bucket) {

if (pair.key === key) {
pair.val = val;
return;
}
}
// 若无该 key ,则将键值对添加至尾部
const pair = new Pair(key, val);
bucket.push(pair);
this.#size++;
}

/* 删除操作 */
remove(key) {
const index = this.#hashFunc(key);
let bucket = this.#buckets[index];
// 遍历桶,从中删除键值对
for (let pair of bucket) {
if (pair.key === key) {
this.#buckets[index] = bucket.filter(
(pair) => pair.key !== key
);
this.#size--;
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filter method goes through every pair in the bucket twice (once for the for...of loop and once for the filter), splice will be better

}
}

/* 扩容哈希表 */
#extend() {
// 暂存原哈希表
const bucketsTmp = this.#buckets;
// 初始化扩容后的新哈希表
this.#capacity *= this.#extendRatio;
this.#buckets = new Array(this.#capacity).fill(null).map((x) => []);
this.#size = 0;
// 将键值对从原哈希表搬运至新哈希表
for (let bucket of bucketsTmp) {
for (let pair of bucket) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let bucket of bucketsTmp) {
for (let pair of bucket) {
for (const bucket of bucketsTmp) {
for (const pair of bucket) {

this.put(pair.key, pair.val);
}
}
}

/* 打印哈希表 */
print() {
for (let bucket of this.#buckets) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let bucket of this.#buckets) {
for (const bucket of this.#buckets) {

let res = [];
for (let pair of bucket) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let res = [];
for (let pair of bucket) {
const res = [];
for (const pair of bucket) {

res.push(pair.key + ' -> ' + pair.val);
}
console.log(res);
}
}
}

/* Driver Code */
/* 初始化哈希表 */
const map = new HashMapChaining();

/* 添加操作 */
// 在哈希表中添加键值对 (key, value)
map.put(12836, '小哈');
map.put(15937, '小啰');
map.put(16750, '小算');
map.put(13276, '小法');
map.put(10583, '小鸭');
console.log('\n添加完成后,哈希表为\nKey -> Value');
map.print();

/* 查询操作 */
// 向哈希表输入键 key ,得到值 value
const name = map.get(13276);
console.log('\n输入学号 13276 ,查询到姓名 ' + name);

/* 删除操作 */
// 在哈希表中删除键值对 (key, value)
map.remove(12836);
console.log('\n删除 12836 后,哈希表为\nKey -> Value');
map.print();
162 changes: 162 additions & 0 deletions codes/javascript/chapter_hashing/hash_map_open_addressing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* File: hash_map_open_addressing.js
* Created Time: 2023-08-06
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 键值对 Number -> String */
class Pair {
constructor(key, val) {
this.key = key;
this.val = val;
}
}

/* 开放寻址哈希表 */
class HashMapOpenAddressing {
#size; // 键值对数量
#capacity; // 哈希表容量
#loadThres; // 触发扩容的负载因子阈值
#extendRatio; // 扩容倍数
#buckets; // 桶数组
#removed; // 删除标记

/* 构造方法 */
constructor() {
this.#size = 0;
this.#capacity = 4;
this.#loadThres = 2.0 / 3.0;
this.#extendRatio = 2;
this.#buckets = new Array(this.#capacity).fill(null);
this.#removed = new Pair(-1, '-1');
}

/* 哈希函数 */
#hashFunc(key) {
return key % this.#capacity;
}

/* 负载因子 */
#loadFactor() {
return this.#size / this.#capacity;
}

/* 查询操作 */
get(key) {
const index = this.#hashFunc(key);
// 线性探测,从 index 开始向后遍历
for (let i = 0; i < this.#capacity; i++) {
// 计算桶索引,越过尾部返回头部
const j = (index + i) % this.#capacity;
// 若遇到空桶,说明无此 key ,则返回 null
if (this.#buckets[j] === null) return null;
// 若遇到指定 key ,则返回对应 val
if (
this.#buckets[j]?.key === key &&
this.#buckets[j]?.[key] !== this.#removed.key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.#buckets[j]?.key === key &&
this.#buckets[j]?.[key] !== this.#removed.key
this.#buckets[j].key === key &&
this.#buckets[j].[key] !== this.#removed.key

)
return this.#buckets[j].val;
}
return null;
}

/* 添加操作 */
put(key, val) {
// 当负载因子超过阈值时,执行扩容
if (this.#loadFactor() > this.#loadThres) {
this.#extend();
}
const index = this.#hashFunc(key);
// 线性探测,从 index 开始向后遍历
for (let i = 0; i < this.#capacity; i++) {
// 计算桶索引,越过尾部返回头部
let j = (index + i) % this.#capacity;
// 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶
if (
this.#buckets[j] === null ||
this.#buckets[j][key] === this.#removed.key
) {
this.#buckets[j] = new Pair(key, val);
this.#size += 1;
return;
}
// 若遇到指定 key ,则更新对应 val
if (this.#buckets[j]?.key === key) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (this.#buckets[j]?.key === key) {
if (this.#buckets[j].key === key) {

this.#buckets[j].val = val;
return;
}
}
}

/* 删除操作 */
remove(key) {
const index = this.#hashFunc(key);
// 线性探测,从 index 开始向后遍历
for (let i = 0; i < this.#capacity; i++) {
// 计算桶索引,越过尾部返回头部
const j = (index + i) % this.#capacity;
// 若遇到空桶,说明无此 key ,则直接返回
if (this.#buckets[j] === null) {
return;
}
// 若遇到指定 key ,则标记删除并返回
if (this.#buckets[j]?.key === key) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (this.#buckets[j]?.key === key) {
if (this.#buckets[j].key === key) {

this.#buckets[j] = this.#removed;
this.#size -= 1;
return;
}
}
}

/* 扩容哈希表 */
#extend() {
// 暂存原哈希表
const bucketsTmp = this.#buckets;
// 初始化扩容后的新哈希表
this.#capacity *= this.#extendRatio;
this.#buckets = new Array(this.#capacity).fill(null);
this.#size = 0;
// 将键值对从原哈希表搬运至新哈希表
for (let pair of bucketsTmp) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let pair of bucketsTmp) {
for (const pair of bucketsTmp) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to be updated

if (pair !== null && pair.key !== this.#removed.key) {
this.put(pair.key, pair.val);
}
}
}

/* 打印哈希表 */
print() {
for (let pair of this.#buckets) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let pair of this.#buckets) {
for (const pair of this.#buckets) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to be updated

if (pair !== null) {
console.log(pair.key + ' -> ' + pair.val);
} else {
console.log('null');
}
}
}
}

/* Driver Code */
/* 初始化哈希表 */
let map = new HashMapOpenAddressing();

/* 添加操作 */
// 在哈希表中添加键值对 (key, value)
map.put(12836, '小哈');
map.put(15937, '小啰');
map.put(16750, '小算');
map.put(13276, '小法');
map.put(10583, '小鸭');
console.log('\n添加完成后,哈希表为\nKey -> Value');
map.print();

/* 查询操作 */
// 向哈希表输入键 key ,得到值 value
const name = map.get(13276);
console.log('\n输入学号 13276 ,查询到姓名 ' + name);

/* 删除操作 */
// 在哈希表中删除键值对 (key, value)
map.remove(16750);
console.log('\n删除 16750 后,哈希表为\nKey -> Value');
map.print();
60 changes: 60 additions & 0 deletions codes/javascript/chapter_hashing/simple_hash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* File: simple_hash.js
* Created Time: 2023-08-06
* Author: yuan0221 (yl1452491917@gmail.com)
*/

/* 加法哈希 */
function addHash(key) {
let hash = 0;
const MODULUS = 1000000007;
for (let c of key) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let c of key) {
for (const c of key) {

hash = (hash + c.charCodeAt(0)) % MODULUS;
}
return hash;
}

/* 乘法哈希 */
function mulHash(key) {
let hash = 0;
const MODULUS = 1000000007;
for (let c of key) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let c of key) {
for (const c of key) {

hash = (31 * hash + c.charCodeAt(0)) % MODULUS;
}
return hash;
}

/* 异或哈希 */
function xorHash(key) {
let hash = 0;
const MODULUS = 1000000007;
for (let c of key) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let c of key) {
for (const c of key) {

hash ^= c.charCodeAt(0);
}
return hash & MODULUS;
}

/* 旋转哈希 */
function rotHash(key) {
let hash = 0;
const MODULUS = 1000000007;
for (let c of key) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (let c of key) {
for (const c of key) {

hash = ((hash << 4) ^ (hash >> 28) ^ c.charCodeAt(0)) % MODULUS;
}
return hash;
}

/* Driver Code */
const key = 'Hello 算法';

let hash = addHash(key);
console.log('加法哈希值为 ' + hash);

hash = mulHash(key);
console.log('乘法哈希值为 ' + hash);

hash = xorHash(key);
console.log('异或哈希值为 ' + hash);

hash = rotHash(key);
console.log('旋转哈希值为 ' + hash);
Loading