From 1a16bc24fe94cc32b1bb43cb92dd60efd07f8954 Mon Sep 17 00:00:00 2001 From: lixingliang Date: Fri, 1 Mar 2019 15:22:28 +0800 Subject: [PATCH] add 20_lru.go file --- go/20_lru/lru_cache.go | 148 ++++++++++++++++++++++++++++++++++++ go/20_lru/lru_cache_test.go | 67 ++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 go/20_lru/lru_cache.go create mode 100644 go/20_lru/lru_cache_test.go diff --git a/go/20_lru/lru_cache.go b/go/20_lru/lru_cache.go new file mode 100644 index 0000000..4cd826e --- /dev/null +++ b/go/20_lru/lru_cache.go @@ -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) +} diff --git a/go/20_lru/lru_cache_test.go b/go/20_lru/lru_cache_test.go new file mode 100644 index 0000000..45fc93c --- /dev/null +++ b/go/20_lru/lru_cache_test.go @@ -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 +}