【数据结构学习】链表--基于链表的LruCache实现

LRU缓存算法实现
本文详细介绍了一种基于链表的LRU(Least Recently Used)缓存算法实现,包括节点类、缓存映射类及缓存类的设计与应用。通过具体代码示例,展示了如何在Android环境中使用LRU缓存机制进行数据存储与管理。
public class Node<K, T> {

    K key;

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    T value;

    /**
     * 下一个节点
     */
    Node<K, T> next;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public Node() {

    }

    public Node(K key, T value) {
        this.key = key;
        this.value = value;
    }

    public Node<K, T> getNext() {

        return next;
    }

    public void setNext(Node<K, T> next) {
        this.next = next;
    }
}

节点

 

public class LruLinkMap<K, T> {

    /**
     * 头节点
     */
    Node<K, T> mHeader;

    private int mNumber;

    private int currentNumber = 0;

    public LruLinkMap(int number) {
        mNumber = number;
        // 初始化头节点
        mHeader = new Node<>();
    }

    public Node<K, T> getNode(K key) {
        Node<K, T> node = mHeader;
        while (node.next != null) {
            Node<K, T> p = node.next;
            if (p.getKey().equals(key)) {
                return p;
            }
            node = p;
        }
        return null;
    }

    public void insertNode(Node<K, T> node) {
        // 首先先判断是否在队列中,如果在队列中,就把队列中已经有的参数删除,然后将已有的插入队首
        // 如果不在队列中,就先判断当前的大小是否已经达到容量,如果没有插入到队首
        // 如果达到容量,就删除队尾,将新元素插入队首
        if (node == null) {
            return;
        }
        boolean isFull = (currentNumber == mNumber);
        // 从头节点开始便利
        Node<K, T> p = mHeader;
        while (p.next != null) {
            Node<K, T> currentNode = p.next;
            if (currentNode.getKey().equals(node.getKey())) {
                // 找到当前的节点是已经存在的节点,删除当前节点
                Log.d("zpb", "缓存已经满了,删除已经存在的节点{" + currentNode.getKey() + ":" + currentNode.getValue() + "}");

                p.next = currentNode.next;
                currentNumber--;
                break;
            } else if (isFull && currentNode.next == null) {
                Log.d("zpb", "缓存已经满了,删除尾节点{" + currentNode.getKey() + ":" + currentNode.getValue() + "}");
                // 如果当前是尾节点
                p.next = currentNode.next;
                // 删除当前节点
                currentNumber--;
                break;
            }
            p = currentNode;

        }
        // 然后将节点插入头节点
        Node<K, T> next = mHeader.next;
        mHeader.next = node;
        node.next = next;
        currentNumber++;

    }

    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("{");
        Node<K, T> node = mHeader;
        while (node.next != null) {
            Node<K, T> p = node.next;
            stringBuffer.append(p.getKey() + ":" + p.getValue() + ",");

            node = p;

        }
        stringBuffer.append("}");
        return stringBuffer.toString();
    }

}

缓存map

 

package com.example.meitu.data;

import android.util.Log;

/**
 * Created by meitu on 2019/1/21.
 */

public class LRUCache<K, T> {
    private int maxNumber = 100;

    private LruLinkMap<K, T> lruCache;

    public LRUCache(int maxNumber) {
        this.maxNumber = maxNumber;
        lruCache = new LruLinkMap<>(maxNumber);
    }

    public void put(Node<K, T> node) {
        lruCache.insertNode(node);
    }

    public Node<K,T> get(K key) {
        return lruCache.getNode(key);
    }

    public void print(){
        Log.d("zpb","cache:"+lruCache.toString());
    }
}

缓存类

 

public class MainActivity extends AppCompatActivity {
    LRUCache<String, String> mCache;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCache = new LRUCache<>(5);
        test();
    }

    public void test() {
        Log.d("zpb", "添加1");

        mCache.put(new Node<String, String>("1", "noe"));
        mCache.print();
        Log.d("zpb", "添加2");

        mCache.put(new Node<String, String>("2", "two"));
        mCache.print();
        Log.d("zpb", "添加3");

        mCache.put(new Node<String, String>("3", "three"));
        mCache.print();
        Log.d("zpb", "添加4");

        mCache.put(new Node<String, String>("4", "four"));
        mCache.print();
        mCache.print();
        Log.d("zpb", "此处替换");
        Log.d("zpb","2="+mCache.get("2").getValue());
        mCache.put(new Node<String, String>("2", "no for"));
        Log.d("zpb","2="+mCache.get("2").getValue());
        mCache.print();
        mCache.put(new Node<String, String>("5", "five"));
        mCache.print();
        Log.d("zpb", "此处满了");
        mCache.put(new Node<String, String>("6", "six"));
        mCache.print();

    }
}

测试类

### 基于链表数据结构应用场景及其实现 #### 双循环链表的应用场景及其初始化过程 双循环链表是一种特殊的线性数据结构,在其中每个节点不仅指向下一个节点还指向前一个节点,并且首尾相接形成闭环。这种特性使得双向遍历变得高效,同时也简化了一些操作逻辑。 对于空链表的构建而言,这一步骤至关重要因为它设定了后续所有增删查改的基础框架[^1]: ```python class Node: def __init__(self, data=None): self.data = data self.next = None self.prev = None def init_empty_doubly_circular_linked_list(): head = Node() head.next = head head.prev = head return head ``` 这段代码展示了如何创建一个没有任何实际元素仅包含头结点(`head`) 的双循环链表实例。这里通过让 `next` 和 `prev` 都指向自己来表示这是一个封闭环路的状态。 #### 数据结构作为接口的概念解释 从更广泛的角度来看待数据结构的作用时可以发现它们不仅仅是简单的容器而是提供了特定功能和服务给其他程序组件使用的工具集。正如定义所言,数据结构能够充当不同算法间的桥梁——即一种抽象层面上的操作契约;也可以理解成一组被良好封装起来的方法集合用于管理和访问内部保存的信息[^2]。 #### 关联两个非具体概念的例子:缓存淘汰机制中的LRU Cache与FIFO Queue 当提到“两个非”的时候如果是指两种不同的策略或原则,则可以通过考察内存管理领域内的缓存替换政策来进行说明。例如最常用的两种方式分别是最近最少使用(LRU Least Recently Used) 缓存和先进先出(FIFO First-First-Out)队列: ##### LRU Cache 实现 (基于哈希映射 + 双向链表) 为了有效地追踪哪些项目是最久未被访问过的以便在达到容量上限时移除这些条目,可以采用组合了散列表(hash map)快速查找特性和双向链表顺序排列优势的设计方案。每当有新的键值对加入或是已有记录得到更新时都将对应节点移动到链表头部位置从而保持最新活动状态;而一旦超出设定大小则直接丢弃位于末端的部分即可完成自动清理工作。 ```python from collections import OrderedDict class LRUCache(OrderedDict): def __init__(self, capacity: int): super().__init__() self.capacity = capacity def get(self, key: str) -> any: if key not in self: return -1 value = super().pop(key) self[key] = value # Move to end as most recently used. return value def put(self, key: str, value: any) -> None: if key in self: del self[key] elif len(self) >= self.capacity: oldest_key = next(iter(self)) del self[oldest_key] self[key] = value ``` 此版本利用 Python 内置模块实现了相同的功能需求但更加简洁明了易于维护。 ##### FIFO Queue 应用 (纯链表形式) 相比之下另一种较为朴素的做法就是严格按照时间先后次序决定进出顺序,也就是常说的一端入另一端出的原则。这类模型非常适合用来模拟排队等候服务的过程比如打印任务分配、消息传递系统等场合下都能见到其身影。 ```python class QueueNode: def __init__(self, item): self.item = item self.next = None class LinkedListQueue: class Empty(Exception): pass def __init__(self): self._front = None self._rear = None self.size = 0 def enqueue(self, element): newest = QueueNode(element) if self.is_empty(): self._front = newest else: self._rear.next = newest self._rear = newest self.size += 1 def dequeue(self): if self.is_empty(): raise LinkedListQueue.Empty('The queue is empty.') answer = self._front.item self._front = self._front.next self.size -= 1 if self.is_empty(): self._rear = None return answer def front(self): if self.is_empty(): raise LinkedListQueue.Empty('The queue is empty.') return self._front.item def is_empty(self): return self._front is None ``` 上述例子分别阐述了两种典型却截然不同的设计理念应用于解决相似问题的不同方面。前者强调的是效率优化后者则是遵循简单直观的思想路线。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值