散列表-LRU缓存淘汰算法

LRU缓存淘汰算法,也就是最近最少使用缓存淘汰策略实现的算法。

借助于链表,我们可以这样做:

我们可以维护一个按照访问时间从大到小有序排列的链表。当然在这算法中这个链表有长度限制,以便于当链表容量达到时执行淘汰策略。

无论是①在缓存中保存数据  ②从缓存中删除一个数据  ③在缓存中查到一个数据,这三种都要先进行缓存中数据的查找操作,即

添加时:我们先要在缓存中查找这个数据是否存在。如果没有找到则直接将数据放到链表头部,如果找到的话,将这个数据移动到链表头部。

删除时:我们先要在缓存中查找这个数据是否存在。如果找到则删除这个数据。

查找时:我们先要在缓存中查找这个数据是否存在。如果找到则返回这个数据。

所以,如果单纯使用链表去实现LRU缓存淘汰算法,时间复杂度是O(n).

为了提高时间复杂度,我们可以借助于散列表,散列表查找数据时间复杂度O(1),这里使用散列表加双向链表来实现LRU缓存淘汰算法。

添加时:我们首先根据key在散列表中找到要删除的节点,时间复杂度O(1)。找到后将这个数据移动到链表头部。找不到则添加到链表头部,如果链表满的话还要删除链表的尾结点。

删除时:我们在时间复杂度O(1)内找到这个数据然后执行删除。

查找时:直接根据key在时间复杂度为O(1)内就可以找到了。

代码:

package com.study.algorithm.hashtable;

import java.util.HashMap;

/**
 * @Auther: JeffSheng
 * @Date: 2019/8/30 11:23
 * @Description:
 * 基于散列表+双向链表 实现LRU算法
 *
 */
public class LruBaseHashTable<K,V> {

    /**
     * 默认链表容量
     */
    private final static Integer DEFAULT_CAPACITY = 10;


    /**
     * 头结点
     */
    private DoubleNode<K,V> headNode;


    /**
     * 尾结点
     */
    private DoubleNode<K,V> tailNode;

    /**
    * 链表长度
    */
    private Integer length;

    /**
     * 链表容量
     */
    private Integer capacity;


    /**
     * 散列表存储key以及对应链表
     */
    private HashMap<K,DoubleNode<K,V>> table;


    static class DoubleNode<K,V>{
        private K key;

        private V value;

        private DoubleNode<K,V> prev;

        private DoubleNode<K,V> next;

        DoubleNode(){}

        DoubleNode(K key,V value){
            this.key=key;
            this.value=value;
        }

    }

    /**
     * 初始化散列表
     * @param capacity
     */
    public LruBaseHashTable(int capacity) {
        this.length = 0;
        this.capacity = capacity;
        headNode = new DoubleNode<>();
        tailNode = new DoubleNode<>();

        headNode.next = tailNode;
        tailNode.prev = headNode;

        table = new HashMap<>();
    }

    public LruBaseHashTable() {
        this(DEFAULT_CAPACITY);
    }


    /**
     * 新增节点
     * @param key
     * @param value
     */
    public void add(K key,V value){
        DoubleNode<K,V> node = table.get(key);
        if(node==null){
            DoubleNode<K,V> newNode=new DoubleNode<>(key,value);
            table.put(key,newNode);
            //将新结点插入到链表头部
            addNodeToHead(newNode);

            //如果链表容量已超
            if(++length >capacity){
                //从散列表删除尾结点元素
                DoubleNode<K,V> tail = popTail();
                table.remove(tail.key);
                length--;
            }

        }else{
            //添加的节点存在,则覆盖此节点值
            node.value = value;
            //然后将存在的节点移动到链表头部
            moveToHead(node);
        }

    }


    /**
     * 将节点移动到头部
     *
     * @param node
     */
    private void moveToHead(DoubleNode<K, V> node) {
        removeNode(node);
        addNodeToHead(node);
    }



    /**
     * 弹出尾部数据节点
     */
    private DoubleNode<K, V> popTail() {
        DoubleNode<K, V> node = tailNode.prev;
        removeNode(node);
        return node;
    }

    /**
     * 移除节点
     *
     * @param node
     */
    private void removeNode(DoubleNode<K, V> node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }


    /**
     * 将新节点添加到链表头部
     * @param newNode
     */
    public void addNodeToHead(DoubleNode<K,V> newNode){

        newNode.next = headNode.next;
        newNode.prev=headNode;

        headNode.next.prev=newNode;
        headNode.next = newNode;
    }


    /**
     * 从散列表获取节点值
     * @param key
     * @return
     */
    public V get(K key){
        DoubleNode<K,V> node = table.get(key);
        if (node == null) {
            return null;
        }
        //节点存在移动到头部
        moveToHead(node);
        return node.value;
    }

    /**
     * 从散列移除节点数据
     *
     * @param key
     */
    public void remove(K key) {
        DoubleNode<K, V> node = table.get(key);
        if (node == null) {
            return;
        }
        //节点存在则移除此节点
        removeNode(node);
        length--;
    }

    private void printAll() {
        DoubleNode<K, V> node = headNode.next;
        while (node.next != null) {
            System.out.print(node.value + ",");
            node = node.next;
        }
        System.out.println();
    }

}
发布了222 篇原创文章 · 获赞 805 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/shengqianfeng/article/details/100155644