-
Notifications
You must be signed in to change notification settings - Fork 0
/
linkedhashtable.go
224 lines (209 loc) · 5.08 KB
/
linkedhashtable.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package algorithm
import (
"fmt"
"strings"
)
type LinkedHashTable struct {
table []lruNode // 散列表
head *lruNode // 头指针
tail *lruNode // 尾指针
capacity int // 容量
length int // 长度
}
type lruNode struct {
key int
value interface{}
previous *lruNode // 双向链表前驱指针
next *lruNode // 双向链表后继指针
hnext *lruNode // 散列表拉链
}
// 默认容量
const (
DEFAULT_LENGTH = 6
)
func NewLinkedHashTable(capacity int) *LinkedHashTable {
return &LinkedHashTable{
table: make([]lruNode, DEFAULT_LENGTH),
capacity: capacity,
length: 0,
}
}
func (list *LinkedHashTable) Get(key int) interface{} {
if list.head == nil {
return nil
}
if node := list.searchNode(key); node != nil {
list.moveToTail(node)
return node.value
}
return nil
}
func (list *LinkedHashTable) Put(key int, value interface{}) {
if node := list.searchNode(key); node != nil {
// 数据在 LRU 中,更新数据并移动到尾部
node.value = value
list.moveToTail(node)
return
}
// 插入数据
list.insertNode(key, value)
if list.length > list.capacity {
// 删除头部
var headKey int = list.head.key
list.removeNode(headKey)
}
}
// 新增节点 (当前场景前提条件:节点不存在)
// 如果数据已经在缓存中,需要将其移动到双向链表的尾部;
// 如果数据不在缓存中,还要看缓存有没有满。如果满了,则将双向链表头部的结点删除,然后再将数据放到链表的尾部;如果没有满,就直接将数据放到链表的尾部。
func (list *LinkedHashTable) insertNode(key int, value interface{}) {
newNode := &lruNode{
key: key,
value: value,
}
// 哈希值
hashCode := hash(key)
node := &list.table[hashCode]
newNode.hnext = node.hnext
list.table[hashCode].hnext = newNode
// 新节点位于尾部
tail := list.tail
if tail == nil {
// 空链表
list.head = newNode
list.tail = newNode
} else {
// 非空链表
newNode.previous = tail
tail.next = newNode
list.tail = newNode
}
list.length++
}
// 删除节点
// 借助散列表,可以在 O(1) 时间复杂度里找到要删除的结点。
// 借助双向链表,可以通过前驱指针 O(1) 时间复杂度获取前驱结点,然后进行删除。
func (list *LinkedHashTable) removeNode(key int) {
if list.head == nil {
return
}
// 从哈希表中删除
hashCode := hash(key)
node := &list.table[hashCode]
current := node.hnext
for current != nil && current.key != key {
node = current
current = current.hnext
}
if current == nil {
return
}
node.hnext = current.hnext
// 从链表中删除
// 前一节点的next
node.previous.next = node.next
// 后一节点的previous
node.next.previous = node.previous
// avoid memory leaks
node.next = nil
node.previous = nil
node.value = nil
list.length--
}
// 查找节点
// 散列表中查找数据的时间复杂度接近 O(1),通过散列表可以很快地在缓存中找到一个数据。
// 当找到数据之后,需要将它移动到双向链表的尾部。
func (list *LinkedHashTable) searchNode(key int) *lruNode {
if list.tail == nil {
return nil
}
// 哈希值
hashCode := hash(key)
node := &list.table[hashCode]
if node == nil {
return nil
}
current := node.hnext
for current != nil {
if current.key == key {
return current
}
current = current.hnext
}
return nil
}
// 移到尾部
func (list *LinkedHashTable) moveToTail(node *lruNode) {
if list.tail == node {
return
}
if list.head == node {
list.head = node.next
list.head.previous = nil
} else {
node.next.previous = node.previous
node.previous.next = node.next
}
node.next = nil
list.tail.next = node
node.previous = list.tail
// 尾节点
list.tail = node
}
func (list *LinkedHashTable) Print() string {
if list.head == nil {
return ""
}
current := list.head
// 值
var message string
for current != nil {
message += fmt.Sprintf("%+v", current.value)
current = current.next
if current != nil {
message += "->"
}
}
return message
}
func (list *LinkedHashTable) PrintLinkedlist() string {
if list.head == nil {
return ""
}
current := list.head
var builder strings.Builder
for current != nil {
builder.WriteString(fmt.Sprintf("%p【key:%d,value:%v,hash:%d,hnext:%p】", current, current.key, current.value, hash(current.key), current.hnext))
current = current.next
if current != nil {
builder.WriteString("->")
}
}
return builder.String()
}
func (list *LinkedHashTable) PrintHashtable() string {
if list.head == nil {
return ""
}
var address []string
var builder strings.Builder
for index, val := range list.table {
builder.Reset()
current := val.hnext
for current != nil {
builder.WriteString(fmt.Sprintf("%p", current))
current = current.hnext
if current != nil {
builder.WriteString("->")
}
}
if builder.Len() > 0 {
address = append(address, fmt.Sprintf("%s【bucket%d】", builder.String(), index))
}
}
return strings.Join(address, " => ")
}
// 哈希函数
func hash(key int) int {
return (key ^ (key >> 32)) & (DEFAULT_LENGTH - 1)
}