commit
67b6efd5e1
148
go/20_lru/lru_cache.go
Normal file
148
go/20_lru/lru_cache.go
Normal file
@ -0,0 +1,148 @@
|
||||
package lru_cache
|
||||
|
||||
const (
|
||||
hostbit = uint64(^uint(0)) == ^uint64(0)
|
||||
LENGTH = 100
|
||||
)
|
||||
|
||||
type lruNode struct {
|
||||
prev *lruNode
|
||||
next *lruNode
|
||||
|
||||
key int // lru key
|
||||
value int // lru value
|
||||
|
||||
hnext *lruNode // 拉链
|
||||
}
|
||||
|
||||
type LRUCache struct {
|
||||
node []lruNode // hash list
|
||||
|
||||
head *lruNode // lru head node
|
||||
tail *lruNode // lru tail node
|
||||
|
||||
capacity int //
|
||||
used int //
|
||||
}
|
||||
|
||||
func Constructor(capacity int) LRUCache {
|
||||
return LRUCache{
|
||||
node: make([]lruNode, LENGTH),
|
||||
head: nil,
|
||||
tail: nil,
|
||||
capacity: capacity,
|
||||
used: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *LRUCache) Get(key int) int {
|
||||
if this.tail == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
if tmp := this.searchNode(key); tmp != nil {
|
||||
this.moveToTail(tmp)
|
||||
return tmp.value
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (this *LRUCache) Put(key int, value int) {
|
||||
// 1. 首次插入数据
|
||||
// 2. 插入数据不在 LRU 中
|
||||
// 3. 插入数据在 LRU 中
|
||||
// 4. 插入数据不在 LRU 中, 并且 LRU 已满
|
||||
|
||||
if tmp := this.searchNode(key); tmp != nil {
|
||||
tmp.value = value
|
||||
this.moveToTail(tmp)
|
||||
return
|
||||
}
|
||||
this.addNode(key, value)
|
||||
|
||||
if this.used > this.capacity {
|
||||
this.delNode()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *LRUCache) addNode(key int, value int) {
|
||||
newNode := &lruNode{
|
||||
key: key,
|
||||
value: value,
|
||||
}
|
||||
|
||||
tmp := &this.node[hash(key)]
|
||||
newNode.hnext = tmp.hnext
|
||||
tmp.hnext = newNode
|
||||
this.used++
|
||||
|
||||
if this.tail == nil {
|
||||
this.tail, this.head = newNode, newNode
|
||||
return
|
||||
}
|
||||
this.tail.next = newNode
|
||||
newNode.prev = this.tail
|
||||
this.tail = newNode
|
||||
}
|
||||
|
||||
func (this *LRUCache) delNode() {
|
||||
if this.head == nil {
|
||||
return
|
||||
}
|
||||
prev := &this.node[hash(this.head.key)]
|
||||
tmp := prev.hnext
|
||||
|
||||
for tmp != nil && tmp.key != this.head.key {
|
||||
prev = tmp
|
||||
tmp = tmp.hnext
|
||||
}
|
||||
if tmp == nil {
|
||||
return
|
||||
}
|
||||
prev.hnext = tmp.hnext
|
||||
this.head = this.head.next
|
||||
this.head.prev = nil
|
||||
this.used--
|
||||
}
|
||||
|
||||
func (this *LRUCache) searchNode(key int) *lruNode {
|
||||
if this.tail == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查找
|
||||
tmp := this.node[hash(key)].hnext
|
||||
for tmp != nil {
|
||||
if tmp.key == key {
|
||||
return tmp
|
||||
}
|
||||
tmp = tmp.hnext
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *LRUCache) moveToTail(node *lruNode) {
|
||||
if this.tail == node {
|
||||
return
|
||||
}
|
||||
if this.head == node {
|
||||
this.head = node.next
|
||||
this.head.prev = nil
|
||||
} else {
|
||||
node.next.prev = node.prev
|
||||
node.prev.next = node.next
|
||||
}
|
||||
|
||||
node.next = nil
|
||||
this.tail.next = node
|
||||
node.prev = this.tail
|
||||
|
||||
this.tail = node
|
||||
}
|
||||
|
||||
func hash(key int) int {
|
||||
if hostbit {
|
||||
return (key ^ (key >> 32)) & (LENGTH - 1)
|
||||
}
|
||||
return (key ^ (key >> 16)) & (LENGTH - 1)
|
||||
}
|
67
go/20_lru/lru_cache_test.go
Normal file
67
go/20_lru/lru_cache_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package lru_cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Hostbit(t *testing.T) {
|
||||
fmt.Println(hostbit)
|
||||
}
|
||||
|
||||
func Test_LRU(t *testing.T) {
|
||||
lru := Constructor(2)
|
||||
|
||||
lru.Put(1, 1)
|
||||
lru.Put(2, 2)
|
||||
assert.Equal(t, lru.Get(1), 1) // returns 1
|
||||
lru.Put(3, 3) // evicts key 2
|
||||
assert.Equal(t, lru.Get(2), -1) // returns -1 (not found)
|
||||
lru.Put(4, 4) // evicts key 1
|
||||
assert.Equal(t, lru.Get(1), -1) // returns -1 (not found)
|
||||
assert.Equal(t, lru.Get(3), 3) // returns 3
|
||||
assert.Equal(t, lru.Get(4), 4) // returns 4
|
||||
}
|
||||
|
||||
func Test_LRU_PutGet(t *testing.T) {
|
||||
lru := Constructor(1)
|
||||
|
||||
lru.Put(1, 2)
|
||||
assert.Equal(t, lru.Get(1), 2) // returns 2
|
||||
}
|
||||
|
||||
func Test_LRU_PutGetPutGetGet(t *testing.T) {
|
||||
lru := Constructor(1)
|
||||
|
||||
lru.Put(2, 1)
|
||||
assert.Equal(t, lru.Get(2), 1) // returns 1
|
||||
lru.Put(3, 2)
|
||||
assert.Equal(t, lru.Get(2), -1) // returns -1
|
||||
assert.Equal(t, lru.Get(3), 2) // returns 2
|
||||
}
|
||||
|
||||
func Test_LRU_PPGPPG(t *testing.T) {
|
||||
lru := Constructor(2)
|
||||
|
||||
lru.Put(2, 1)
|
||||
lru.Put(2, 2)
|
||||
assert.Equal(t, lru.Get(2), 2) // returns 2
|
||||
lru.Put(1, 4)
|
||||
lru.Put(4, 1)
|
||||
assert.Equal(t, lru.Get(2), -1) // returns -1
|
||||
assert.Equal(t, lru.Get(3), -1) // returns -1
|
||||
}
|
||||
|
||||
func Test_LRU_PPGPPG_2(t *testing.T) {
|
||||
lru := Constructor(2)
|
||||
|
||||
lru.Put(2, 1)
|
||||
lru.Put(2, 2)
|
||||
assert.Equal(t, lru.Get(2), 2) // returns 2
|
||||
lru.Put(1, 1)
|
||||
lru.Put(4, 1)
|
||||
assert.Equal(t, lru.Get(2), -1) // returns -1
|
||||
assert.Equal(t, lru.Get(3), -1) // returns -1
|
||||
}
|
Loading…
Reference in New Issue
Block a user