Skip to content

Commit 48a0d57

Browse files
feat:add lfu cache alogrithm (TheAlgorithms#695)
* feat:add lfu cache alogrithm * feat:add the functions and types of LFU in README.md * fix: unexport initItem func --------- Co-authored-by: Taj <[email protected]>
1 parent 5f887c5 commit 48a0d57

File tree

4 files changed

+194
-2
lines changed

4 files changed

+194
-2
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,16 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
8989

9090
##### Functions:
9191

92-
1. [`NewLRU`](./cache/lru.go#L20): NewLRU represent initiate lru cache with capacity
92+
1. [`NewLRU`](./cache/lru.go#L22): NewLRU represent initiate lru cache with capacity
93+
2. [`NewLFU`](./cache/lfu.go#L33): NewLFU represent initiate lfu cache with capacity
94+
3. [`Get`](./cache/lfu.go#L52): Get the value by key from LFU cache
95+
4. [`Put`](./cache/lfu.go#L66): Put the key and value in LFU cache
9396

9497
---
9598
##### Types
9699

97-
1. [`LRU`](./cache/lru.go#L12): No description provided.
100+
1. [`LRU`](./cache/lru.go#L15): Default the struct of lru cache algorithm.
101+
2. [`LFU`](./cache/lfu.go#L19): Default the struct of lfu cache algorithm.
98102

99103

100104
---

cache/lfu.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// lfu.go
2+
// description: a type of cache algorithm used to manage memory within a computer.
3+
// details:
4+
// The standard characteristics of this method involve the system keeping track of the number of times a block is referenced in memory.
5+
// When the cache is full and requires more room the system will purge the item with the lowest reference frequency.
6+
// ref: (https://en.wikipedia.org/wiki/Least_frequently_used)
7+
// time complexity: O(N)
8+
// space complexity: O(1)
9+
// author: [CocaineCong](https://github.com/CocaineCong)
10+
11+
package cache
12+
13+
import (
14+
"container/list"
15+
"math"
16+
)
17+
18+
// LFU the Least Frequently Used (LFU) page-replacement algorithm
19+
type LFU struct {
20+
len int // length
21+
cap int // capacity
22+
minFreq int // The element that operates least frequently in LFU
23+
24+
// key: key of element, value: value of element
25+
itemMap map[string]*list.Element
26+
27+
// key: frequency of possible occurrences of all elements in the itemMap
28+
// value: elements with the same frequency
29+
freqMap map[int]*list.List
30+
}
31+
32+
// NewLFU init the LFU cache with capacity
33+
func NewLFU(capacity int) LFU {
34+
return LFU{
35+
len: 0,
36+
cap: capacity,
37+
minFreq: math.MaxInt,
38+
itemMap: make(map[string]*list.Element),
39+
freqMap: make(map[int]*list.List),
40+
}
41+
}
42+
43+
// initItem to init item for LFU
44+
func initItem(k string, v any, f int) item {
45+
return item{
46+
key: k,
47+
value: v,
48+
freq: f,
49+
}
50+
}
51+
52+
// Get the key in cache by LFU
53+
func (c *LFU) Get(key string) any {
54+
// if existed, will return value
55+
if e, ok := c.itemMap[key]; ok {
56+
// the frequency of e +1 and change freqMap
57+
c.increaseFreq(e)
58+
obj := e.Value.(item)
59+
return obj.value
60+
}
61+
62+
// if not existed, return nil
63+
return nil
64+
}
65+
66+
// Put the key in LFU cache
67+
func (c *LFU) Put(key string, value any) {
68+
if e, ok := c.itemMap[key]; ok {
69+
// if key existed, update the value
70+
obj := e.Value.(item)
71+
obj.value = value
72+
c.increaseFreq(e)
73+
} else {
74+
// if key not existed
75+
obj := initItem(key, value, 1)
76+
// if the length of item gets to the top line
77+
// remove the least frequently operated element
78+
if c.len == c.cap {
79+
c.eliminate()
80+
c.len--
81+
}
82+
// insert in freqMap and itemMap
83+
c.insertMap(obj)
84+
// change minFreq to 1 because insert the newest one
85+
c.minFreq = 1
86+
// length++
87+
c.len++
88+
}
89+
}
90+
91+
// increaseFreq increase the frequency if element
92+
func (c *LFU) increaseFreq(e *list.Element) {
93+
obj := e.Value.(item)
94+
// remove from low frequency first
95+
oldLost := c.freqMap[obj.freq]
96+
oldLost.Remove(e)
97+
// change the value of minFreq
98+
if c.minFreq == obj.freq && oldLost.Len() == 0 {
99+
// if it is the last node of the minimum frequency that is removed
100+
c.minFreq++
101+
}
102+
// add to high frequency list
103+
c.insertMap(obj)
104+
}
105+
106+
// insertMap insert item in map
107+
func (c *LFU) insertMap(obj item) {
108+
// add in freqMap
109+
l, ok := c.freqMap[obj.freq]
110+
if !ok {
111+
l = list.New()
112+
c.freqMap[obj.freq] = l
113+
}
114+
e := l.PushFront(obj)
115+
// update or add the value of itemMap key to e
116+
c.itemMap[obj.key] = e
117+
}
118+
119+
// eliminate clear the least frequently operated element
120+
func (c *LFU) eliminate() {
121+
l := c.freqMap[c.minFreq]
122+
e := l.Back()
123+
obj := e.Value.(item)
124+
l.Remove(e)
125+
126+
delete(c.itemMap, obj.key)
127+
}

cache/lfu_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cache_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/TheAlgorithms/Go/cache"
7+
)
8+
9+
func TestLFU(t *testing.T) {
10+
lfuCache := cache.NewLFU(3)
11+
t.Run("Test 1: Put number and Get is correct", func(t *testing.T) {
12+
key, value := "1", 1
13+
lfuCache.Put(key, value)
14+
got := lfuCache.Get(key)
15+
16+
if got != value {
17+
t.Errorf("expected: %v, got: %v", value, got)
18+
}
19+
})
20+
21+
t.Run("Test 2: Get data not stored in cache should return nil", func(t *testing.T) {
22+
got := lfuCache.Get("2")
23+
if got != nil {
24+
t.Errorf("expected: nil, got: %v", got)
25+
}
26+
})
27+
28+
t.Run("Test 3: Put data over capacity and Get should return nil", func(t *testing.T) {
29+
lfuCache.Put("2", 2)
30+
lfuCache.Put("3", 3)
31+
lfuCache.Put("4", 4)
32+
33+
got := lfuCache.Get("1")
34+
if got != nil {
35+
t.Errorf("expected: nil, got: %v", got)
36+
}
37+
})
38+
39+
t.Run("test 4: Put key over capacity but recent key exists", func(t *testing.T) {
40+
lfuCache.Put("4", 4)
41+
lfuCache.Put("3", 3)
42+
lfuCache.Put("2", 2)
43+
lfuCache.Put("1", 1)
44+
45+
got := lfuCache.Get("4")
46+
if got != nil {
47+
t.Errorf("expected: nil, got: %v", got)
48+
}
49+
50+
expected := 3
51+
got = lfuCache.Get("3")
52+
53+
if got != expected {
54+
t.Errorf("expected: %v, got: %v", expected, got)
55+
}
56+
57+
})
58+
}

cache/lru.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import (
77
type item struct {
88
key string
99
value any
10+
11+
// the frequency of key
12+
freq int
1013
}
1114

1215
type LRU struct {

0 commit comments

Comments
 (0)