美文网首页
LRU和LFU(Go和CPP版本)

LRU和LFU(Go和CPP版本)

作者: 小幸运Q | 来源:发表于2021-01-01 12:31 被阅读0次

LRU

  • LRU本来用deque写的,但是deque在有删除操作的时候还是莫名其妙指向end()前一位(实际上已经是第二位了),没办法,所以只好用list

插入:检查是否是末尾,是的话直接改值返回,不是的话看是否溢出,溢出的话删头节点对应的map然后pop,如果没溢出,如果本来就有这个key,删掉list对应元素留着map等后面再映射,然后添加到list末尾然后增加map映射。

读取:如果有这个值那就插入这个值更新然后return该值,如果没,那就返回-1

list+pair版本:(C++)

#include<iostream>
#include<list>
#include<deque>
#include<unordered_map>
using namespace std;
class LRUCache {
public:
    int maxsize;
    list<pair<int,int>>d;
    unordered_map<int,list<pair<int,int>>::iterator>m;
    LRUCache(int capacity) {
        maxsize=capacity;
    }
    
    int get(int key) {
        // get到的时候修改排列顺序
        if(m.count(key)){
            put(key,m[key]->second);     
            return m[key]->second;
        }else{
            return -1;
        }
    }
    
    void put(int key, int value) {
        // 如果本来就是末尾那就直接改value
        if(d.back().first==key){
            d.back().second=value;
            return;
        }
        if(m.count(key)){
            d.erase(m[key]);
        }
        else if(maxsize==d.size()){
            m.erase(d.front().first);
            d.pop_front();
        }
        d.push_back({key,value});
        m[key]=--d.end();
    }
};

int main() {
    // LRUCache* lRUCache = new LRUCache(2);
    // lRUCache->put(1, 1); // 缓存是 {1=1}
    // lRUCache->put(2, 2); // 缓存是 {1=1, 2=2}
    // std::cout<<lRUCache->get(1)<<endl;    // 返回 1
    // lRUCache->put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
    // std::cout<<lRUCache->get(2)<<endl;    // 返回 -1 (未找到)
    // lRUCache->put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
    // std::cout<<lRUCache->get(1)<<endl;    // 返回 -1 (未找到)
    // std::cout<<lRUCache->get(3)<<endl;    // 返回 3
    // std::cout<<lRUCache->get(4)<<endl;    // 返回 4
    LRUCache* lRUCache = new LRUCache(3);
    lRUCache->put(1,1);
    lRUCache->put(2,2);
    lRUCache->put(3,3);
    lRUCache->put(4,4);
    cout<<(lRUCache->get(4))<<endl;
    cout<<(lRUCache->get(3))<<endl;
    cout<<(lRUCache->get(2))<<endl;
    cout<<(lRUCache->get(1))<<endl;
    lRUCache->put(5,5);
    cout<<(lRUCache->get(1))<<endl;
    cout<<(lRUCache->get(2))<<endl;
    cout<<(lRUCache->get(3))<<endl;
    cout<<(lRUCache->get(4))<<endl;
    cout<<(lRUCache->get(5))<<endl;
}

List+map版本:(Go)

package main

import (
    "container/list"
    "fmt"
)

type Node struct {
    key   int
    value int
}
type LRUCache struct {
    m    map[int]*list.Element
    l    *list.List
    size int
}

func Constructor(capacity int) LRUCache {
    return LRUCache{
        m:    make(map[int]*list.Element),
        l:    list.New(),
        size: capacity,
    }
}

func (this *LRUCache) Get(key int) int {
    if _, ok := this.m[key]; !ok {
        fmt.Println(-1)
        return -1
    }
    n := Node{key, this.m[key].Value.(Node).value}
    this.l.Remove(this.m[key])
    delete(this.m, this.m[key].Value.(Node).key)
    this.l.PushBack(n)
    this.m[key] = this.l.Back()
    fmt.Println(this.m[key].Value.(Node).value)
    return this.m[key].Value.(Node).value
}

func (this *LRUCache) Put(key int, value int) {
    if _, ok := this.m[key]; ok {
        if this.m[key].Value.(Node).value != value {
            n := Node{key, value}
            this.l.Remove(this.m[key])
            this.l.PushBack(n)
            this.m[key] = this.l.Back()
        } else {
            this.Get(key)
        }
    } else {
        if this.size == len(this.m) {
            k := this.l.Front().Value.(Node).key
            this.l.Remove(this.l.Front())
            delete(this.m, k)
        }
        n := Node{key, value}
        this.l.PushBack(n)
        this.m[key] = this.l.Back()
    }
    //fmt.Println("put",this.size,key,this.m)
}

func main() {
    lRUCache := Constructor(2)
    //lRUCache.Put(1, 1) // 缓存是 {1=1}
    //lRUCache.Put(2, 2) // 缓存是 {1=1, 2=2}
    //lRUCache.Get(1)    // 返回 1
    //lRUCache.Put(3, 3) // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
    //lRUCache.Get(2)    // 返回 -1 (未找到)
    //lRUCache.Put(4, 4) // 该操作会G使得关键字 1 作废,缓存是 {4=4, 3=3}
    //lRUCache.Get(1)    // 返回 -1 (未找到)
    //lRUCache.Get(3)    // 返回 3
    //lRUCache.Get(4)    // 返回 4

    lRUCache.Get(2)
    lRUCache.Put(2, 6)
    lRUCache.Get(1)
    lRUCache.Put(1, 5)
    lRUCache.Put(1, 2)
    lRUCache.Get(1)
    lRUCache.Get(2)
}

LFU

go语言版本

package main

import (
    "container/list"
    "fmt"
)

type Node struct {
    key    int // 因为list里面不知道是哪个key的node,删除的时候很麻烦,所以添加key标记
    value  int
    counts int
}
type LFUCache struct {
    min_freq int
    size     int
    // key -> value+counts
    KeyToNode map[int]Node
    // key's frequency -> list of key Node
    FreqToKeyList map[int]*list.List
}

func Constructor(capacity int) *LFUCache {
    return &LFUCache{
        min_freq:      1, // 最低的频率优先淘汰
        size:          capacity,
        KeyToNode:     make(map[int]Node),
        FreqToKeyList: make(map[int]*list.List), // list int
    }
}
func (this *LFUCache) Get(key int) int {
    if v, ok := this.KeyToNode[key]; ok {
        var next *list.Element
        // 利用key删除旧freq List中的老freq
        for e := this.FreqToKeyList[v.counts].Front(); e != nil; e = next {
            next = e.Next()
            if e.Value.(Node).key == key {
                this.FreqToKeyList[v.counts].Remove(e)
            }
        }
        // 被删除的可能是min_freq,这时候需要修改min_freq
        if this.FreqToKeyList[v.counts].Len() == 0 {
            if v.counts == this.min_freq {
                this.min_freq = v.counts + 1
            }
            // delete(this.FreqToKeyList,v.counts) 想想还是算了,留着复用影像也不大
        }
        // 添加Node到新的freq+1
        node := this.KeyToNode[key]
        node.counts += 1
        this.KeyToNode[key] = node

        // v.counts变成旧freq了
        freq := this.KeyToNode[key].counts
        if _, ok := this.FreqToKeyList[freq]; ok {
            this.FreqToKeyList[freq].PushBack(this.KeyToNode[key])
        } else {
            n := Node{key, this.KeyToNode[key].value, 1}
            l := list.New()
            l.PushBack(n)
            this.FreqToKeyList[freq] = l
        }
        fmt.Println(key, v.value)
        return v.value
    } else {
        fmt.Println(-1)
        return -1
    }
}

// 按key来算访问,key同value不同算访问同一个
// put到队尾,size溢出删除队头
func (this *LFUCache) Put(key int, value int) {
    if this.size <= 0 {
        return
    }
    if _, ok := this.KeyToNode[key]; ok {
        // 有对应项 , 借用Get更新freq++ , 然后修改value
        this.Get(key)
        a := this.KeyToNode[key]
        a.value = value
        this.KeyToNode[key] = a
        for e := this.FreqToKeyList[this.KeyToNode[key].counts].Front(); e != nil; e = e.Next() {
            if e.Value.(Node).key == key {
                e.Value = Node{key, value, 1}
                // e.Value.(Node).value = value  无法直接修改map对象的属性,只能覆盖
            }
        }
    } else {
        // 没有对应的项 , 如果硬塞会溢出那就删掉min_freq尾巴上的node , 然后塞进 freq=1 的list
        if len(this.KeyToNode) == this.size {
            // 需要删除min_freq List的队头node
            delete(this.KeyToNode, this.FreqToKeyList[this.min_freq].Front().Value.(Node).key)
            this.FreqToKeyList[this.min_freq].Remove(this.FreqToKeyList[this.min_freq].Front())
        }
        if _, ok := this.FreqToKeyList[1]; ok {

        } else {
            // 没list就加
            this.FreqToKeyList[1] = list.New()
        }
        // min_freq肯定为1了,前面删除需要旧值所以就放后面修改
        this.min_freq = 1

        // 因为KeyToNode为空就添加新node
        n := Node{key, value, 1}
        this.KeyToNode[key] = n
        this.FreqToKeyList[1].PushBack(n)
    }
}
func main() {
    Cache := Constructor(2)
    Cache.Put(1, 1)
    Cache.Put(2, 2)
    Cache.Get(1)    // 返回 1
    Cache.Put(3, 3) // 去除键 2
    Cache.Get(2)    // 返回 -1(未找到)
    Cache.Get(3)    // 返回 3
    Cache.Put(4, 4) // 去除键 1
    Cache.Get(1)    // 返回 -1(未找到)
    Cache.Get(3)    // 返回 3
    Cache.Get(4)    // 返回 4
}

相关文章

  • LRU和LFU(Go和CPP版本)

    LRU LRU本来用deque写的,但是deque在有删除操作的时候还是莫名其妙指向end()前一位(实际上已经是...

  • LRU和LFU的CPP实现

    LRU(least recently used cache) LFU(least frequently used ...

  • LRU和LFU

    LRU means Least Recently UsedLFU means Least Frequently Used

  • Algorithm进阶计划 -- LRU 与 LFU 算法

    LRU 与 LFU 算法LRU 算法LFU 算法 1. LRU 算法 LRU 算法是一种缓存淘汰策略,是 Leas...

  • [LeetCode] LRU和LFU详解之一

    LRU (Least recently used,最近最少使用 ) 和 LFU (Least frequently...

  • LRU 和 LFU 缓存淘汰算法

    缓存淘汰算法有很多种,这里只简单介绍2种:LRU 和 LFU。 LRU全称 Least recently used...

  • LRU算法原理与实践

    简介 操作系统中进行内存管理中时采用一些页面置换算法,如LRU、LFU和FIFO等。其中LRU应用较为广泛。LRU...

  • groupcache 源码系列三 LRU

    关于LRU,可以参考LRU LFU FIFO LRU全称是Least Recently Used,即最近最久未使用...

  • 缓存策略之LRU和LFU

    为什么 缓存,就是把数据存储在本地,简单实用key-value做一个映射就好了。但为什么还要缓存策略,因为缓存的大...

  • 缓存淘汰算法 LRU 和 LFU

    缓存是一个计算机思维,对于重复的计算,缓存其结果,下次再算这个任务的时候,不去真正的计算,而是直接返回结果,能加快...

网友评论

      本文标题:LRU和LFU(Go和CPP版本)

      本文链接:https://www.haomeiwen.com/subject/ayehbktx.html