LRU算法代码实现记录一下

366 阅读3分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

前言

  • 之前有写过一篇关于LRU淘汰算法的文章 LRU算法记录一下,LRU算法思想是淘汰最近最少使用的元素,当一个元素在一段时间内没有访问过后,那么在之后的一段时间也极有可能不会被访问,然后当数据池满了后将其淘汰掉。
  • 这次把关于LFU算法的三种不同实现方式写一下,那废话不多说,show me code

LRU实现之数组 O(N)

/**
 * LRU实现之数组 O(N)
 *
 * @Author: ZRH
 * @Date: 2021/8/13 10:32
 */
public class ArrayLru {

    /**
     * 初始数组
     */
    private static Node[] arrNode;
    /**
     * 数组长度限制大小
     */
    private final static Integer SIZE = 3;

    private static void add (Object param) {
        if (null == param) {
            throw new NullPointerException("param must not be null");
        }
        Node node = new Node(param);
        // 添加第一个元素,初始化数组,并把第一个元素放入数组
        if (arrNode == null) {
            arrNode = new Node[SIZE];
            arrNode[0] = node;
            return;
        }
        // 最大num数的数组下标
        int index = 0;
        // 标识是否已经前置赋值
        boolean fal = true;
        for (int i = 0; i < SIZE; i++) {
            // 当前节点为null,并且添加的新节点还未赋值到数组,则赋值
            if (null == arrNode[i]) {
                if (fal) {
                    arrNode[i] = node;
                    fal = false;
                }
                break;
            }

            // 添加的新节点为当前节点,则直接改num
            if (fal && arrNode[i].value.equals(node.value)) {
                arrNode[i].num = 0;
                fal = false;
                break;
            }

            // 找到最大num的数组下标
            if (i == 0) {
                index = i;
            } else if (arrNode[i - 1].num < arrNode[i].num) {
                index = i;
            }
            // 为数组其他所有节点num+1
            arrNode[i].addNum();
        }
        // 标识新加入的节点已经赋值,直接跳出
        if (!fal) {
            return;
        }
        // 把最大num元素替换为新加入的元素(num数越大,说明元素就没有重新赋值为0 ,说明就最久没有使用)
        arrNode[index] = node;
    }

    @Data
    static class Node<T> {
        private final T value;
        private Integer num;

        public Node (T t) {
            this.value = t;
            this.num = 0;
        }

        public void addNum () {
            this.num = this.num + 1;
        }
    }

    public static void main (String[] args) {
        add("a");
        System.out.println(JSON.toJSONString(arrNode));
        add("b");
        System.out.println(JSON.toJSONString(arrNode));
        add("c");
        System.out.println(JSON.toJSONString(arrNode));
        add("c");
        System.out.println(JSON.toJSONString(arrNode));
        add("d");
        System.out.println(JSON.toJSONString(arrNode));
        add("e");
        System.out.println(JSON.toJSONString(arrNode));
    }
}
---------------
打印结果:
[{"num":0,"value":"a"},null,null]
[{"num":1,"value":"a"},{"num":0,"value":"b"},null]
[{"num":2,"value":"a"},{"num":1,"value":"b"},{"num":0,"value":"c"}]
[{"num":3,"value":"a"},{"num":2,"value":"b"},{"num":0,"value":"c"}]
[{"num":0,"value":"d"},{"num":3,"value":"b"},{"num":1,"value":"c"}]
[{"num":1,"value":"d"},{"num":0,"value":"e"},{"num":2,"value":"c"}]
  • 这里数组实现算法还有优化的点:当最后插入新数据时数组已满了,然后发现数组有两个数据的访问标识相等且都值都是最大,那么还应该在这两个数据中找到在数组里待的时间较长的数据,然后把这个数据进行淘汰。

LRU实现之链表 O(N)

/**
 * LRU实现之链表 O(N)
 *
 * @Author: ZRH
 * @Date: 2021/8/13 11:11
 */
public class LinkedLru {

    /**
     * 初始链表
     */
    private static LinkedList<Object> list;
    /**
     * 链表长度限制大小
     */
    private final static Integer SIZE = 3;

    private static void add (Object param) {
        if (null == param) {
            throw new NullPointerException("param must not be null");
        }
        // 添加第一个元素时,初始化链表,并放入元素
        if (null == list) {
            list = new LinkedList<>();
            list.addFirst(param);
            return;
        }
        // 遍历链表,若添加的元素在链表中已存在,则把元素移动到首位
        for (Object o : list) {
            if (o.equals(param)) {
                list.remove(param);
                list.addFirst(param);
                return;
            }
        }
        // 若链表元素大小达到限制,则删除链表尾元素
        if (list.size() == SIZE) {
            list.removeLast();
        }
        // 添加元素到头节点
        list.addFirst(param);

    }

    public static void main (String[] args) {
        add("a");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("c");
        System.out.println(JSON.toJSONString(list));
        add("a");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("d");
        System.out.println(JSON.toJSONString(list));
    }
}
--------------
打印结果:
["a"]
["b","a"]
["c","b","a"]
["a","c","b"]
["b","a","c"]
["b","a","c"]
["b","a","c"]
["d","b","a"]

LRU实现之hash O(1)

/**
 * LRU实现之hash O(1)
 *
 * @Author: ZRH
 * @Date: 2021/8/13 11:32
 */
public class HashLru {

    /**
     * 将添加的元素放入hash中,用于快速查找定位
     * 这里key自定义生成,value是链表节点
     */
    private static Map<String, Node> map;
    /**
     * 存放元素的链表,用于快速删除和插入
     * 这里放入的节点可以包含:元素和map中的key。这样设计是可以通过链表能定位map ,通过map也能快速定位链表
     */
    private static LinkedList<Node> list;
    /**
     * 链表长度限制大小
     */
    private final static Integer SIZE = 3;

    private static void add (Object param) {
        if (null == param) {
            throw new NullPointerException("param must not be null");
        }

        // 添加第一个元素时,初始化hash表和链表,并放入元素
        String key = String.valueOf(param);
        Node firstNode = new Node(key, param);
        if (null == map) {
            map = new HashMap<>(8);
            list = new LinkedList<>();
            map.put(key, firstNode);
            list.addFirst(firstNode);
            return;
        }
        // 通过hash查找当前添加的元素是否已经存在hash中,如果存在,则移动元素到链表头节点
        Node node = map.get(key);
        if (null != node) {
            list.remove(node);
            list.addFirst(node);
            return;
        }
        // 链表中元素大小达到限制长度,则删除链表中尾节点元素
        if (list.size() == SIZE) {
            Node lastNode = list.getLast();
            map.remove(lastNode.key);
            list.remove(lastNode);
        }
        // 头节点插入新元素
        map.put(key, firstNode);
        list.addFirst(firstNode);
    }

    @Data
    static class Node {

        @JSONField(serialize = false)
        private String key;
        private Object value;

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

    public static void main (String[] args) {
        add("a");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("c");
        System.out.println(JSON.toJSONString(list));
        add("a");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("b");
        System.out.println(JSON.toJSONString(list));
        add("d");
        System.out.println(JSON.toJSONString(list));
    }
}
--------------
打印结果:
[{"value":"a"}]
[{"value":"b"},{"value":"a"}]
[{"value":"c"},{"value":"b"},{"value":"a"}]
[{"value":"a"},{"value":"c"},{"value":"b"}]
[{"value":"b"},{"value":"a"},{"value":"c"}]
[{"value":"b"},{"value":"a"},{"value":"c"}]
[{"value":"b"},{"value":"a"},{"value":"c"}]
[{"value":"d"},{"value":"b"},{"value":"a"}]

最后

  • 上诉的代码可以直接copy,就可以运行得到结果,不同实现方式的思想和细节也在代码备注里面
  • 如果上诉代码有什么错误的地方欢迎提出,我也会加以改正
  • 虚心学习,共同进步 -_-