1.已实现 ¶
实现了基本的散列计算(直接挪用Java中HashMap的散列公式)
实现了数据被访问后移动至链尾
实现了节点移除函数,后续供淘汰策略使用
2.未实现 ¶
散列表的动态扩缩容、散列因子
散列表碰撞处理
链表中的元素淘汰策略(热点数据移至链表尾,链头数据为非热点数据,移除尾部即可)
3.特点及适用场景 ¶
通过结合数组(查询快)和链表(删改快)优势,查询时间复杂度近似O(1)
数组的存放数据为链表头的指针
适合无序数据存放
4.具体代码 ¶
package LRUCacheOut
import (
"fmt"
)
// Hashable 是一个接口,要求实现它的类型有一个 HashCode 方法。
type Hashable interface {
HashCode() int
}
type Node struct {
key int
value int
next *Node
prev *Node
}
// String 是一个示例类型,用来表示包含字符串值和缓存的哈希码的结构体。
type String struct {
value []rune // 字符数组
hash int // 缓存的哈希码,初始值为0,表示还没有计算
}
// HashCode 实现了 Hashable 接口,计算并返回字符串的哈希码。
// 如果哈希码已经计算过(即不为0),直接返回缓存的值。
func (s *String) HashCode() int {
if s.hash == 0 && len(s.value) > 0 {
for _, c := range s.value {
s.hash = 31*s.hash + int(c)
}
}
return s.hash
}
// Hash 函数根据提供的键(必须实现了 Hashable 接口)和散列表的容量,
// 计算并返回散列表的索引。
func Hash(key Hashable, capacity int) int {
h := key.HashCode()
if capacity <= 0 {
panic("capacity must be greater than 0")
}
// (h ^ (h >>> 16)) & (capacity -1) 的Go语言实现
//99302346 & 15
//101111010110011101111001010 & 000000000000000000000001111
//10111101011 ^ 10111101011
return (h ^ (h >> 16)) & (capacity - 1)
}
func add(head, newNode *Node) {
current := head
for {
if current.next == nil {
// 记录当前指针
break
} else {
current = current.next
}
}
current.next = newNode
current.next.prev = current
}
func query(head **Node, key int) int {
if *head == nil {
fmt.Println("List is empty")
return -1
}
var tmp *Node
current := *head
for current != nil {
if current.prev == nil {
//暂存当前节点
tmp = current.next
if tmp.key == key {
return tmp.value
}
}
if current.key == key {
// if the current node is not head node ,move to head
// 4 -> 5
current.prev.next = current.next
// 5 <- 4
if current.next != nil {
current.next.prev = current.prev
}
// current node move to the head
current.next = tmp
current.prev = *head
tmp.prev = current
(*head).next = current
(*head).prev = nil
return current.value
}
current = current.next
}
return current.value
}
func delete(head *Node, key int) {
for current := head; current != nil; current = current.next {
if current.key == key {
current.prev.next = current.next
current.next.prev = current.prev
}
}
}
func queryHashTable(types []rune, key int, head *Node) (int, bool) {
str := &String{
value: types,
}
capacity := 16 // 假设散列表的大小为16
index := Hash(str, capacity)
hashTable := make([]*Node, 16)
hashTable[index] = head
result := query(&head, key)
fmt.Printf("查找类型: %c\n对应散列槽位: %d\n散列表存放的头指针地址: %p\n原始的头指针地址 %p\n对应值: %d\n",
types, index, hashTable[index], head, result)
return result, true
}
func Traverse(head *Node) {
current := head
for current != nil {
fmt.Println(current.key, current.value, "上一个内存地址", current.prev, "下一个内存地址", current.next)
current = current.next
}
}
5.单元测试 ¶
package LRUCacheOut
import (
"testing"
)
// 测试 add 函数能否正确地向链表中添加节点
func TestAdd(t *testing.T) {
head := &Node{} // 创建一个空的头节点作为dummy head
// 向链表中添加一个节点
add(head, &Node{key: 1, value: 100})
// 验证是否正确添加了节点
if head.next == nil {
t.Errorf("No node was added.")
} else if head.next.value != 100 {
t.Errorf("Node with value 100 was not added correctly. Got value %d", head.next.value)
}
}
// 测试 queryHashTable 函数能否正确地查询并返回预期的结果
func TestQueryHashTable(t *testing.T) {
head := &Node{key: 0} // 初始化头节点
// 构建链表
add(head, &Node{key: 1, value: 175})
add(head, &Node{key: 2, value: 180})
add(head, &Node{key: 3, value: 170})
qType := []rune("friend")
// 假设queryHashTable现在返回一个值和一个布尔标志
value, found := queryHashTable(qType, 2, head)
if !found {
t.Errorf("Expected to find the key %d, but it was not found.", 1)
}
if value != 180 {
t.Errorf("Expected value %d for key %d, got %d", 180, 2, value)
}
}