LRU和LFU算法实现原理

LRU和LFU算法实现原理


前提

午休的时候刷了下技术文章,看到 LRU和LFU算法。大概看了下原理于是尝试重现一下。


一、LRU是什么

LRU是一种淘汰算法,全称是Least Recently Used。

二、LRU算法的思想

  • 算法的思想就是:如果一个数据在最近一段时间没有被访问到,
    那么在将来它被访问的可能性也很小。
    所以,当指定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。

二、LRU实现

  • 根据算法思想实现需要满足已下条件
    • 1.新增key变为最近使用,当空间已存满数据,删除不常访问的key。
    • 2.获取值时快速找到某个key是否存在,存在key变为最近使用,并返回其对应的 value。

那么那种数据结构满足上面条件,查询快,插入,删除快? 双向链表+哈希表

内存中数据结构如下:
在这里插入图片描述

  • 代码实现如下:
/**
 * LRU全称Least Recently Used(最近最少使用),用来淘汰不常用数据,保留热点数据
 *
 */
public class MyLRUCache {

    private int capacity;

    private int count;

    private Node first;

    private Node last;

    private HashMap<String, Node> nodeHashMap;

    public MyLRUCache(int capacity) {
        this.nodeHashMap = new HashMap<>(capacity);
        this.capacity = capacity;

        Node firsNode = new Node();
        firsNode.prev = null;
        first = firsNode;

        Node lastNode = new Node();
        lastNode.next = null;
        last = lastNode;

        first.next = last;
        last.prev = first;

    }

    public void set(String string, Object value) {
        Node node = nodeHashMap.get(string);
        if (node == null) {
            Node newNode = new Node(string, value);
            addFirst(newNode);
            nodeHashMap.put(string, newNode);
            ++count;
            if (count > capacity) {
                //空间已满,删除不常访问的节点
                Node last = this.last.prev;
                this.nodeHashMap.remove(last.key);
                this.removeNode(last);
                --count;
            }
        } else {
            node.value = value;
            removeOldToAddFirst(node);
        }
    }

    public Object get(String key) {
        Node node = nodeHashMap.get(key);
        if (node == null) {
            return null;
        }
        // 将访问的节点移动到头节点
        this.removeOldToAddFirst(node);
        return node.value;
    }

    /**
     * 删除节点
     *
     * @param node
     */
    private void removeNode(Node node) {
        Node pre = node.prev;
        Node post = node.next;

        pre.next = post;
        post.prev = pre;
    }

    /**
     * 删除指定节点并将该节点添加到头节点
     *
     * @param node
     */
    private void removeOldToAddFirst(Node node) {
        this.removeNode(node);
        this.addFirst(node);
    }

    /**
     * 在该列表开头插入指定的元素
     */
    private void addFirst(Node node) {
        node.prev = first;
        node.next = first.next;

        first.next.prev = node;
        first.next = node;

    }

    @Data
    static class Node {
        private String key;
        private Object value;

        public Node(String key, Object value) {
            this.key = key;
            this.value = value;
        }

        public Node() {
        }

        //前驱节点
        Node prev;
        //后继点
        Node next;

    }


    public String viewString() {
        StringBuilder builder = new StringBuilder();
        Node node = first.next;
        for (; node != null; node = node.next) {
            if (Objects.nonNull(node.value)) {
                builder.append(node.value).append(" ");
            }
        }
        return "MyLRUCache{" + builder.toString() +
                '}';
    }


    public static void main(String[] args) {
        MyLRUCache cache = new MyLRUCache(3);
        cache.set("1", "1");
        cache.set("2", "2");
        cache.set("3", "3");
        cache.set("4", "4");
        System.out.println(cache.viewString()); //MyLRUCache{4 3 2 }
    }
}

一、LFU是什么

LFU是一种淘汰算法(最近最少使用),全称是Least Frequently Used.

二、LFU算法的思想

  • 算法的思想就是:如果一个数据在最近一段时间内访问次数很少,
    那么在将来它被访问的可能性也很小。
    所以,当指定的空间已存满数据时,应当把访问次数很少的数据淘汰。
    这里可以看出LRU和LFU的区别一个是访问数据的顺序,一个是数据的访问次数

二、LFU实现

  • 根据算法思想实现需要满足已下条件
    • 1.新增key(默认访问次数1),当空间已存满数据,删除访问次数最少时间最长的key。
    • 2.获取值时快速找到某个key是否存在,存在key(访问次数加+1),并返回其对应的 value。
    • 3.多个key可能具有相同的访问次数

那么那种数据结构满足上面条件,查询快,插入,删除快?哈希表

内存中数据结构如下:

在这里插入图片描述

  • 代码实现如下:
/**
 * LFU是一种淘汰算法(最近最少使用),全称是Least Frequently Used.
 */
public class MyLFUCache {

    private int capacity;

    private int count;

    private Integer freqMin;

    private HashMap<String, Node> nodeHashMap;

    private HashMap<Integer, LinkedList<Node>> freqHashMap;

    public MyLFUCache(int capacity) {
        this.capacity = capacity;
        this.freqMin = 1;
        int hashMapCapacity = new Double(Math.ceil(capacity / 0.75)).intValue() + 1;
        this.nodeHashMap = new HashMap<>(hashMapCapacity);
        this.freqHashMap = new HashMap<>(hashMapCapacity);
    }

    /**
     * 新增node
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        Node node = nodeHashMap.get(key);
        if (node == null) {
            //更新最少访问频率
            upDataFreqMin();
            node = new Node(key, value);
            addNode(node);
            ++count;
            if (count > capacity) {
                //容量已满淘汰数据
                weedOut();
                --count;
            }
        } else {
            //覆盖旧值
            node.setValue(value);
            addNodeFreq(node);
        }
    }

    private void weedOut() {
        LinkedList<Node> linkedList = freqHashMap.get(freqMin);
        nodeHashMap.remove(linkedList.getLast().getKey());
        linkedList.removeLast();
    }

    /***
     * 更新最少访问的频率
     */
    private void upDataFreqMin() {
        Set<Integer> keySet = freqHashMap.keySet();
        if (keySet.size() > 0) {
            this.freqMin = keySet.stream().reduce(Integer::min).get();
        }
    }

    public Object get(String key) {
        Node node = nodeHashMap.get(key);
        if (node == null) {
            return null;
        }
        addNodeFreq(node);
        return node.getValue();
    }

    /**
     * 删除节点
     */
    private void removeNode(Node node) {
        nodeHashMap.remove(node.getKey(), node);
        LinkedList<Node> nodes = freqHashMap.get(node.freq);
        nodes.remove(node);
        if (nodes.size() == 0) {
            freqHashMap.remove(node.freq);
        }
    }

    private void addNode(Node node) {
        nodeHashMap.put(node.getKey(), node);
        LinkedList<Node> linkedList = freqHashMap.get(node.freq);
        if (CollectionUtils.isEmpty(linkedList)) {
            linkedList = new LinkedList<>();
            freqHashMap.put(node.getFreq(), linkedList);
        }
        linkedList.addFirst(node);
    }

    private void addNodeFreq(Node node) {
        removeNode(node);
        node.setFreq(node.getFreq() + 1);
        addNode(node);
        upDataFreqMin();
    }

    @Data
    static class Node {
        private String key;
        private Object value;
        private Integer freq;

        public Node(String key, Object value) {
            this.key = key;
            this.value = value;
            this.freq = 1;
        }

        private Node() {
        }
    }


    public void viewString() {
        StringBuilder builder = new StringBuilder();
        builder.append(" ");
        List<Integer> list = new ArrayList<>(freqHashMap.keySet());
        list.sort((a, b) -> {
            if (a < b) {
                return 1;
            } else if (a > b) {
                return -1;
            }
            return 0;
        });
        for (Integer integer : list) {
            LinkedList<Node> nodes = freqHashMap.get(integer);
            for (Node node : nodes) {
                builder.append(node.key).append("=>").append(node.freq).append(" ");
            }
        }
        System.out.println("MyLFUCache{" + builder.toString() +
                '}');
    }


    public static void main(String[] args) {
        MyLFUCache cache = new MyLFUCache(3);
        cache.set("a", "a");
        cache.set("a", "a");
        cache.set("a", "a");
        cache.set("b", "b");
        cache.set("b", "b");
        cache.set("c", "c");
        cache.set("c", "c");
        cache.set("d", "d");
        cache.viewString(); //MyLFUCache{ a=>3 c=>2 d=>1 }
    }
}

总结

感谢您的阅读

如果你发现了错误的地方,可以在留言区提出来,我对其加以修改

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值