Java实现LFU缓存算法

本文详细介绍了LFU(Least Frequently Used)缓存淘汰算法的实现,通过链表和HashMap结合的方式,确保了插入和查询操作的时间复杂度为O(1)。LFU算法优先淘汰使用频率最低的元素,当频率相同时,淘汰最久未使用的元素。文章还提供了完整的Java代码实现,包括LFUCache类、FreqNode类和DataNode类,展示了如何维护数据节点和频率节点的双向链表。

LFU算法会淘汰使用频率最低的元素, 相同频率就淘汰最久未使用的元素
插入和查询时间复杂度都要求O(1),因此使用 链表+HashMap实现

public class LFUCache {

    private Map<Integer, DataNode> dataMap;
    private Map<Integer, FreqNode> freqMap;
    private int capacity;
    private int size;
    private int minFrequency;

    public LFUCache(int capacity) {
        this.capacity = capacity;
        freqMap = new HashMap<>();
        dataMap = new HashMap<>();
    }

    /**
     * 查询数据, 如果不存在, 返回-1
     */
    public int get(int key) {
        DataNode dataNode = dataMap.get(key);
        if (dataNode == null) return -1; // 不存在
        markUse(dataNode); // 标记频次+1
        return dataNode.val;
    }

    public void put(int key, int value) {
        DataNode dataNode = dataMap.get(key);
        if (dataNode == null) {
            add(key, value); // 不存在, 新增
        } else {
            dataNode.val = value; // 存在, 频次+1
            markUse(dataNode);
        }
    }

    // 新增节点
    private void add(int key, int value) {
        DataNode dataNode = new DataNode(key, value);
        dataMap.put(key, dataNode);
        add0(dataNode);
        size++;
        eliminate();
        minFrequency = 0;
    }

    private void add0(DataNode dataNode) {
        FreqNode freqNode = freqMap.get(dataNode.getFrequency());
        if (freqNode == null) {
            freqNode = new FreqNode(dataNode.getFrequency());
            freqMap.put(freqNode.getFrequency(), freqNode);
        }
        freqNode.add(dataNode);
    }

    private void eliminate() {
        if (size <= capacity) {
            return;
        }
        size--;
        FreqNode freqNode = freqMap.get(minFrequency);
        DataNode remove = freqNode.tail.pre;
        remove.remove();
        dataMap.remove(remove.key);
        if (freqNode.isEmpty()) {
            freqMap.remove(minFrequency);
        }
    }

    private void markUse(DataNode dataNode) {
        int frequency = dataNode.getFrequency();
        FreqNode freqNode = freqMap.get(dataNode.getFrequency());
        dataNode.markUse(); // 数据节点频次+1
        dataNode.remove(); // 数据节点从同频链表上移除, 增加到更惨频次的链表上
        if (freqNode.isEmpty()) {
            if (frequency == minFrequency) {
//                    频率最低的节点没了
                minFrequency = dataNode.getFrequency();
            }
            freqMap.remove(frequency);
        }
        add0(dataNode);
    }


    //        存储相同频率的节点
    private static class FreqNode {
        private DataNode head;
        private DataNode tail;
        private int frequency;

        public FreqNode(int frequency) {
            this.frequency = frequency;
            this.head = new DataNode();
            this.tail = new DataNode();
            this.head.setNext(this.tail);
        }

        public int getFrequency() {
            return frequency;
        }

        private void add(DataNode node) {
            head.append(node);
        }

        public boolean isEmpty() {
            return head.next == tail;
        }
    }

    // 存储数据
    private static class DataNode {
        private int key;
        private int val;
        private int frequency;
        private DataNode pre;
        private DataNode next;

        public DataNode() {
        }

        public int getFrequency() {
            return frequency;
        }

        public DataNode(int key, int val) {
            this.key = key;
            this.val = val;
        }

        public void markUse() {
            frequency++;
        }

        public void append(DataNode node) {
            node.setNext(this.next);
            setNext(node);
        }

        //            从链表上移除, 返回所在的FreqNode
        public void remove() {
            if (pre != null) {
                pre.setNext(next);
                pre = null;
            }
            next = null;
        }

        public void setNext(DataNode next) {
            this.next = next;
            if (next != null) {
                next.pre = this;
            }
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            DataNode cur = this;
            while (cur != null) {
                sb.append(String.format("%s:%s:%s, ", cur.frequency, cur.key, cur.val));
                cur = cur.next;
            }
            return sb.toString();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值