@@ -6,7 +6,6 @@ package chapter_hashing
6
6
7
7
import (
8
8
"fmt"
9
- "strconv"
10
9
)
11
10
12
11
/* 开放寻址哈希表 */
@@ -15,129 +14,113 @@ type hashMapOpenAddressing struct {
15
14
capacity int // 哈希表容量
16
15
loadThres float64 // 触发扩容的负载因子阈值
17
16
extendRatio int // 扩容倍数
18
- buckets []pair // 桶数组
19
- removed pair // 删除标记
17
+ buckets []* pair // 桶数组
18
+ TOMBSTONE * pair // 删除标记
20
19
}
21
20
22
21
/* 构造方法 */
23
22
func newHashMapOpenAddressing () * hashMapOpenAddressing {
24
- buckets := make ([]pair , 4 )
25
23
return & hashMapOpenAddressing {
26
24
size : 0 ,
27
25
capacity : 4 ,
28
26
loadThres : 2.0 / 3.0 ,
29
27
extendRatio : 2 ,
30
- buckets : buckets ,
31
- removed : pair {
32
- key : - 1 ,
33
- val : "-1" ,
34
- },
28
+ buckets : make ([]* pair , 4 ),
29
+ TOMBSTONE : & pair {- 1 , "-1" },
35
30
}
36
31
}
37
32
38
33
/* 哈希函数 */
39
- func (m * hashMapOpenAddressing ) hashFunc (key int ) int {
40
- return key % m .capacity
34
+ func (h * hashMapOpenAddressing ) hashFunc (key int ) int {
35
+ return key % h .capacity // 根据键计算哈希值
41
36
}
42
37
43
38
/* 负载因子 */
44
- func (m * hashMapOpenAddressing ) loadFactor () float64 {
45
- return float64 (m .size ) / float64 (m .capacity )
39
+ func (h * hashMapOpenAddressing ) loadFactor () float64 {
40
+ return float64 (h .size ) / float64 (h .capacity ) // 计算当前负载因子
46
41
}
47
42
48
- /* 查询操作 */
49
- func (m * hashMapOpenAddressing ) get (key int ) string {
50
- idx := m .hashFunc (key )
51
- // 线性探测,从 index 开始向后遍历
52
- for i := 0 ; i < m .capacity ; i ++ {
53
- // 计算桶索引,越过尾部则返回头部
54
- j := (idx + i ) % m .capacity
55
- // 若遇到空桶,说明无此 key ,则返回 null
56
- if m .buckets [j ] == (pair {}) {
57
- return ""
43
+ /* 搜索 key 对应的桶索引 */
44
+ func (h * hashMapOpenAddressing ) findBucket (key int ) int {
45
+ index := h .hashFunc (key ) // 获取初始索引
46
+ firstTombstone := - 1 // 记录遇到的第一个TOMBSTONE的位置
47
+ for h .buckets [index ] != nil {
48
+ if h .buckets [index ].key == key {
49
+ if firstTombstone != - 1 {
50
+ // 若之前遇到了删除标记,则将键值对移动至该索引处
51
+ h .buckets [firstTombstone ] = h .buckets [index ]
52
+ h .buckets [index ] = h .TOMBSTONE
53
+ return firstTombstone // 返回移动后的桶索引
54
+ }
55
+ return index // 返回找到的索引
58
56
}
59
- // 若遇到指定 key ,则返回对应 val
60
- if m .buckets [j ].key == key && m .buckets [j ] != m .removed {
61
- return m .buckets [j ].val
57
+ if firstTombstone == - 1 && h .buckets [index ] == h .TOMBSTONE {
58
+ firstTombstone = index // 记录遇到的首个删除标记的位置
62
59
}
60
+ index = (index + 1 ) % h .capacity // 线性探测,越过尾部则返回头部
61
+ }
62
+ // 若 key 不存在,则返回添加点的索引
63
+ if firstTombstone != - 1 {
64
+ return firstTombstone
65
+ }
66
+ return index
67
+ }
68
+
69
+ /* 查询操作 */
70
+ func (h * hashMapOpenAddressing ) get (key int ) string {
71
+ index := h .findBucket (key ) // 搜索 key 对应的桶索引
72
+ if h .buckets [index ] != nil && h .buckets [index ] != h .TOMBSTONE {
73
+ return h .buckets [index ].val // 若找到键值对,则返回对应 val
63
74
}
64
- // 若未找到 key ,则返回空字符串
65
- return ""
75
+ return "" // 若键值对不存在,则返回 ""
66
76
}
67
77
68
78
/* 添加操作 */
69
- func (m * hashMapOpenAddressing ) put (key int , val string ) {
70
- // 当负载因子超过阈值时,执行扩容
71
- if m .loadFactor () > m .loadThres {
72
- m .extend ()
79
+ func (h * hashMapOpenAddressing ) put (key int , val string ) {
80
+ if h .loadFactor () > h .loadThres {
81
+ h .extend () // 当负载因子超过阈值时,执行扩容
73
82
}
74
- idx := m .hashFunc (key )
75
- // 线性探测,从 index 开始向后遍历
76
- for i := 0 ; i < m .capacity ; i ++ {
77
- // 计算桶索引,越过尾部则返回头部
78
- j := (idx + i ) % m .capacity
79
- // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶
80
- if m .buckets [j ] == (pair {}) || m .buckets [j ] == m .removed {
81
- m .buckets [j ] = pair {
82
- key : key ,
83
- val : val ,
84
- }
85
- m .size += 1
86
- return
87
- }
88
- // 若遇到指定 key ,则更新对应 val
89
- if m .buckets [j ].key == key {
90
- m .buckets [j ].val = val
91
- return
92
- }
83
+ index := h .findBucket (key ) // 搜索 key 对应的桶索引
84
+ if h .buckets [index ] == nil || h .buckets [index ] == h .TOMBSTONE {
85
+ h .buckets [index ] = & pair {key , val } // 若键值对不存在,则添加该键值对
86
+ h .size ++
87
+ } else {
88
+ h .buckets [index ].val = val // 若找到键值对,则覆盖 val
93
89
}
94
90
}
95
91
96
92
/* 删除操作 */
97
- func (m * hashMapOpenAddressing ) remove (key int ) {
98
- idx := m .hashFunc (key )
99
- // 遍历桶,从中删除键值对
100
- // 线性探测,从 index 开始向后遍历
101
- for i := 0 ; i < m .capacity ; i ++ {
102
- // 计算桶索引,越过尾部则返回头部
103
- j := (idx + i ) % m .capacity
104
- // 若遇到空桶,说明无此 key ,则直接返回
105
- if m .buckets [j ] == (pair {}) {
106
- return
107
- }
108
- // 若遇到指定 key ,则标记删除并返回
109
- if m .buckets [j ].key == key {
110
- m .buckets [j ] = m .removed
111
- m .size -= 1
112
- }
93
+ func (h * hashMapOpenAddressing ) remove (key int ) {
94
+ index := h .findBucket (key ) // 搜索 key 对应的桶索引
95
+ if h .buckets [index ] != nil && h .buckets [index ] != h .TOMBSTONE {
96
+ h .buckets [index ] = h .TOMBSTONE // 若找到键值对,则用删除标记覆盖它
97
+ h .size --
113
98
}
114
99
}
115
100
116
101
/* 扩容哈希表 */
117
- func (m * hashMapOpenAddressing ) extend () {
118
- // 暂存原哈希表
119
- tmpBuckets := make ([]pair , len (m .buckets ))
120
- copy (tmpBuckets , m .buckets )
121
-
122
- // 初始化扩容后的新哈希表
123
- m .capacity *= m .extendRatio
124
- m .buckets = make ([]pair , m .capacity )
125
- m .size = 0
102
+ func (h * hashMapOpenAddressing ) extend () {
103
+ oldBuckets := h .buckets // 暂存原哈希表
104
+ h .capacity *= h .extendRatio // 更新容量
105
+ h .buckets = make ([]* pair , h .capacity ) // 初始化扩容后的新哈希表
106
+ h .size = 0 // 重置大小
126
107
// 将键值对从原哈希表搬运至新哈希表
127
- for _ , p := range tmpBuckets {
128
- if p != ( pair {}) && p != m . removed {
129
- m .put (p .key , p .val )
108
+ for _ , pair := range oldBuckets {
109
+ if pair != nil && pair != h . TOMBSTONE {
110
+ h .put (pair .key , pair .val )
130
111
}
131
112
}
132
113
}
133
114
134
115
/* 打印哈希表 */
135
- func (m * hashMapOpenAddressing ) print () {
136
- for _ , p := range m .buckets {
137
- if p != (pair {}) {
138
- fmt .Println (strconv .Itoa (p .key ) + " -> " + p .val )
139
- } else {
116
+ func (h * hashMapOpenAddressing ) print () {
117
+ for _ , pair := range h .buckets {
118
+ if pair == nil {
140
119
fmt .Println ("nil" )
120
+ } else if pair == h .TOMBSTONE {
121
+ fmt .Println ("TOMBSTONE" )
122
+ } else {
123
+ fmt .Printf ("%d -> %s\n " , pair .key , pair .val )
141
124
}
142
125
}
143
126
}
0 commit comments