@ -1,13 +1,14 @@
/ * *
* File : hash_map_open_addressing.ts
* Created Time : 2023 - 08 - 06
* Author : yuan0221 ( yl1452491917 @gmail . com )
* Author : yuan0221 ( yl1452491917 @gmail . com ) , Krahets ( krahets @163 . com )
* /
/* 键值对 Number -> String */
class Pair {
key : number ;
val : string ;
constructor ( key : number , val : string ) {
this . key = key ;
this . val = val ;
@ -16,111 +17,124 @@ class Pair {
/* 开放寻址哈希表 */
class HashMapOpenAddressing {
# size : number ; // 键值对数量
# capacity : number ; // 哈希表容量
# loadThres : number ; // 触发扩容的负载因子阈值
# extendRatio : number ; // 扩容倍数
# buckets : Pair [ ] ; // 桶数组
# removed : Pair ; // 删除标记
private size : number ; // 键值对数量
private capacity : number ; // 哈希表容量
private loadThres : number ; // 触发扩容的负载因子阈值
private extendRatio : number ; // 扩容倍数
private buckets : Array < Pair | null > ; // 桶数组
private TOMBSTONE : Pair ; // 删除标记
/* 构造方法 */
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' ) ;
this . size = 0 ; // 键值对数量
this . capacity = 4 ; // 哈希表容量
this . loadThres = 2.0 / 3.0 ; // 触发扩容的负载因子阈值
this . extendRatio = 2 ; // 扩容倍数
this . buckets = Array ( this . capacity ) . fill ( null ) ; // 桶数组
this . TOMBSTONE = new Pair ( - 1 , '-1' ) ; // 删除标记
}
/* 哈希函数 */
# hashFunc ( key : number ) : number {
return key % this . # capacity ;
private hashFunc ( key : number ) : number {
return key % this . capacity ;
}
/* 负载因子 */
# loadFactor ( ) : number {
return this . # size / this . # capacity ;
private loadFactor ( ) : number {
return this . size / this . capacity ;
}
/* 搜索 key 对应的桶索引 */
private findBucket ( key : number ) : number {
let index = this . hashFunc ( key ) ;
let firstTombstone = - 1 ;
// 线性探测,当遇到空桶时跳出
while ( this . buckets [ index ] !== null ) {
// 若遇到 key ,返回对应桶索引
if ( this . buckets [ index ] ! . key === key ) {
// 若之前遇到了删除标记,则将键值对移动至该索引
if ( firstTombstone !== - 1 ) {
this . buckets [ firstTombstone ] = this . buckets [ index ] ;
this . buckets [ index ] = this . TOMBSTONE ;
return firstTombstone ; // 返回移动后的桶索引
}
return index ; // 返回桶索引
}
// 记录遇到的首个删除标记
if (
firstTombstone === - 1 &&
this . buckets [ index ] === this . TOMBSTONE
) {
firstTombstone = index ;
}
// 计算桶索引,越过尾部返回头部
index = ( index + 1 ) % this . capacity ;
}
// 若 key 不存在,则返回添加点的索引
return firstTombstone === - 1 ? index : firstTombstone ;
}
/* 查询操作 */
get ( key : number ) : string | null {
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
// 搜索 key 对应的桶索引
const index = this . findBucket ( key ) ;
// 若找到键值对,则返回对应 val
if (
this . # buckets [ j ] . key === key &&
this . # buckets [ j ] . key !== this . # removed . key
)
return this . # buckets [ j ] . val ;
this . buckets [ index ] !== null &&
this . buckets [ index ] !== this . TOMBSTONE
) {
return this . buckets [ index ] ! . val ;
}
// 若键值对不存在,则返回 null
return null ;
}
/* 添加操作 */
put ( key : number , val : string ) : void {
// 当负载因子超过阈值时,执行扩容
if ( this . # loadFactor ( ) > this . # loadThres ) {
this . # extend ( ) ;
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 ;
// 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶
// 搜索 key 对应的桶索引
const index = this . findBucket ( key ) ;
// 若找到键值对,则覆盖 val 并返回
if (
this . # buckets [ j ] === null ||
this . # buckets [ j ] . key === this . # removed . key
this . buckets [ index ] !== null &&
this . buckets [ index ] !== this . TOMBSTONE
) {
this . # buckets [ j ] = new Pair ( key , val ) ;
this . # size += 1 ;
return ;
}
// 若遇到指定 key ,则更新对应 val
if ( this . # buckets [ j ] . key === key ) {
this . # buckets [ j ] . val = val ;
this . buckets [ index ] ! . val = val ;
return ;
}
}
// 若键值对不存在,则添加该键值对
this . buckets [ index ] = new Pair ( key , val ) ;
this . size ++ ;
}
/* 删除操作 */
remove ( key : number ) : void {
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 ) {
this . # buckets [ j ] = this . # removed ;
this . # size -= 1 ;
return ;
}
// 搜索 key 对应的桶索引
const index = this . findBucket ( key ) ;
// 若找到键值对,则用删除标记覆盖它
if (
this . buckets [ index ] !== null &&
this . buckets [ index ] !== this . TOMBSTONE
) {
this . buckets [ index ] = this . TOMBSTONE ;
this . size -- ;
}
}
/* 扩容哈希表 */
# extend ( ) : void {
private extend ( ) : void {
// 暂存原哈希表
const bucketsTmp = this . # buckets ;
const bucketsTmp = this . buckets ;
// 初始化扩容后的新哈希表
this . # capacity *= this . # extendRatio ;
this . # buckets = new Array ( this . # capacity ) . fill ( null ) ;
this . # size = 0 ;
this . capacity *= this . extendRatio ;
this . buckets = Array ( this . capacity ) . fill ( null ) ;
this . size = 0 ;
// 将键值对从原哈希表搬运至新哈希表
for ( const pair of bucketsTmp ) {
if ( pair !== null && pair . key !== this . # removed . key ) {
if ( pair !== null && pair !== this . TOMBSTONE ) {
this . put ( pair . key , pair . val ) ;
}
}
@ -128,39 +142,41 @@ class HashMapOpenAddressing {
/* 打印哈希表 */
print ( ) : void {
for ( const pair of this . # buckets ) {
if ( pair !== null ) {
console . log ( pair . key + ' -> ' + pair . val ) ;
} else {
for ( const pair of this . buckets ) {
if ( pair === null ) {
console . log ( 'null' ) ;
} else if ( pair === this . TOMBSTONE ) {
console . log ( 'TOMBSTONE' ) ;
} else {
console . log ( pair . key + ' -> ' + pair . val ) ;
}
}
}
}
/* Driver Code */
/* 初始化哈希表 */
let map = new HashMapOpenAddressing ( ) ;
/* 添加操作 */
// 在哈希表中添加键值对 (key, val ue )
map. put ( 12836 , '小哈' ) ;
map. put ( 15937 , '小啰' ) ;
map. put ( 16750 , '小算' ) ;
map. put ( 13276 , '小法' ) ;
map. put ( 10583 , '小鸭' ) ;
// 初始化哈希表
const hash map = new HashMapOpenAddressing ( ) ;
// 添加操作
// 在哈希表中添加键值对 (key, val )
hash map. put ( 12836 , '小哈' ) ;
hash map. put ( 15937 , '小啰' ) ;
hash map. put ( 16750 , '小算' ) ;
hash map. put ( 13276 , '小法' ) ;
hash map. put ( 10583 , '小鸭' ) ;
console . log ( '\n添加完成后, 哈希表为\nKey -> Value' ) ;
map. print ( ) ;
hash map. print ( ) ;
/* 查询操作 */
// 向哈希表输入键 key ,得到值 val ue
const name = map. get ( 13276 ) ;
// 查询操作
// 向哈希表输入键 key ,得到值 val
const name = hash map. get ( 13276 ) ;
console . log ( '\n输入学号 13276 ,查询到姓名 ' + name ) ;
/* 删除操作 */
// 在哈希表中删除键值对 (key, val ue )
map. remove ( 16750 ) ;
// 删除操作
// 在哈希表中删除键值对 (key, val )
hash map. remove ( 16750 ) ;
console . log ( '\n删除 16750 后,哈希表为\nKey -> Value' ) ;
map. print ( ) ;
hash map. print ( ) ;
export { } ;