通过单链表实现LRU算法

文章介绍了内存缓存的三种淘汰策略——FIFO、LFU和LRU,并详细讲解了LRU算法的实现,包括自定义单链表和基于单链表的LRU算法。LRU算法通过维护一个有序链表,当缓存满时,最近最少使用的数据会被淘汰。
摘要由CSDN通过智能技术生成
内存缓存淘汰机制
  1. FIFO(First In First Out,先进先出)
  2. LFU(Least Frequently Used,最不经常使用)
  3. LRU(Least Recently Used,最近最少使用)

LFU是从整个使用时间内判断使用次数最少的算法,而LRU是从最近一段时间内来判断使用次数最少的算法

LRU算法

  1. 新数据插入到链表的头部
  2. 当缓存命中(即缓存数据被访问),数据要移动到表头
  3. 当链表满的时候,将链表尾部数据丢弃
自定义单链表和继承自自定义单链表的Lru算法链表

1.自定义单链表类MyLinkedList.java

package com.tangkun.lru;

/**
 * 自定义单链表
 * */
public class MyLinkedList<T> {
	//头结点的指针
	public Node<T> node;
	//链表的结点总数量
	public int size;

	public MyLinkedList() {

	}

	/**
	 * 新增 指定索引位置新增
	 */
	public void put(int index, T data) {
		checkPositionIndex(index);
		//插入结点位置的前一个结点
		Node<T> prev = node;
		//插入结点位置原来那个结点,新结点插入后,这个结点就在新结点后面
		Node<T> cur = node;
		//遍历出插入位置上一个结点和原来的结点
		for (int i = 0; i < index; i++) {
			prev = cur;
			cur = cur.next;
		}
		//构建出插入索引位置的新结点,将上面遍历出来的原来结点cur作为新结点的next指针的值
		Node<T> newNode = new Node<>(data, cur);
		//将插入位置上一个结点的next指针指向新插入的结点
		prev.next = newNode;
		//链表长度加1
		size++;
	}

	//新增到头部
	public void putFirst(T data) {
		//插入结点位置原来那个结点,新结点插入后,这个结点就在新结点后面
		Node<T> cur = node;
		Node<T> newNode = new Node<>(data, cur);
		//将插入到头部的结点赋值为整个链表的头结点
		node = newNode;
		size++;
	}

	/**
	 * 删除
	 */
	public void remove(int index) {
		checkPositionIndex(index);
		//删除结点位置的前一个结点
		Node<T> prev = node;
		//删除结点位置原来那个结点
		Node<T> cur = node;
		//遍历出删除位置上一个结点和原来的结点
		for (int i = 0; i < index; i++) {
			prev = cur;
			cur = cur.next;
		}
		prev.next = cur.next;
		cur.next = null;
		//链表长度减1
		size--;
	}

	//删除最后一个结点
	public void removeLast() {
		//删除结点位置的前一个结点
		Node<T> prev = node;
		//删除结点位置原来那个结点
		Node<T> cur = node;
		//遍历出删除位置上一个结点和原来的结点
		for (int i = 0; i < size; i++) {
			prev = cur;
			cur = cur.next;
		}
		prev.next = null;
		//链表长度减1
		size--;
	}

	/**
	 * 修改
	 */
	public T set(int index, T data) {
		checkPositionIndex(index);
		//修改结点位置原来那个结点
		Node<T> cur = node;
		//遍历出修改位置的结点
		for (int i = 0; i < index; i++) {
			cur = cur.next;
		}
		cur.data = data;
		return cur.data;
	}

	/**
	 * 查询
	 */
	public T get(int index) {
		checkPositionIndex(index);
		//查询结点位置原来那个结点
		Node<T> cur = node;
		//遍历出查询位置的结点
		for (int i = 0; i < index; i++) {
			cur = cur.next;
		}
		return cur.data;
	}

	/**
	 * 检查index是否越界
	 */
	public void checkPositionIndex(int index) {
		if (!(index >= 0 && index <= size)) {
			throw new IndexOutOfBoundsException("index:" + index + "|size:" + size);
		}
	}

	@Override
	public String toString() {
		//打印出来所有结点中的数据域
		Node<T> cur = node;
		for (int i = 0; i < size; i++) {
			System.out.println(cur.data + " ");
			cur = cur.next;
		}
		return "";
	}

	static class Node<T> {
		T data;
		Node<T> next;

		Node(T data, Node<T> next) {
			this.data = data;
			this.next = next;
		}
	}

	public static void main(String[] args) {
		MyLinkedList<Integer> myLinkedList = new MyLinkedList<>();
		myLinkedList.putFirst(3);
		myLinkedList.putFirst(2);
		myLinkedList.putFirst(1);
		System.out.println("添加结点后打印:" + myLinkedList.toString() + "\n");
		myLinkedList.put(2, 0);
		System.out.println("在索引为2的位置,添加结点0,然后打印:" + myLinkedList.toString() + "\n");
		myLinkedList.remove(1);
		System.out.println("删除索引为1的结点后打印:" + myLinkedList.toString() + "\n");
		myLinkedList.set(1, 8);
		System.out.println("更改索引1的值为8,然后打印:" + myLinkedList.toString() + "\n");
		myLinkedList.get(1);
		System.out.println("查询索引1的值:" + myLinkedList.get(1) + "\n");
	}
}

2.自定义Lru算法单链表LruLinkedList.java

package com.tangkun.lru;

/**
 * Lru算法单链表
 * */
public class LruLinkedList<T> extends MyLinkedList<T> {

	//自定义缓存容量大小
	public int memorySize;
	//缓存默认容量大小
	public static final int DEFAULT_CAPACITY = 5;

	public LruLinkedList() {
		this(DEFAULT_CAPACITY);
	}

	public LruLinkedList(int capacity) {
		memorySize = capacity;
	}

	/**
	 * Lru新增
	 * 新数据插入到链表的头部
	 */
	public void lruPutFirst(T data) {
		//判断当前缓存占用大小是否等于最大缓存容量,如果小于,则memorySize加1;若大于等于,则memorySize=MAX_CAPACITY,同时移除链表中最后一个结点
		if (size < memorySize) {
		} else {
			//移除最后一个结点
			removeLast();
		}
		//新增结点到头部
		putFirst(data);
	}

	/**
	 * Lru 查询
	 * 当缓存命中(即缓存数据被访问),数据要移动到表头
	 */
	public T lruGet(int index) {
		checkPositionIndex(index);
		//当前索引index前一个结点
		Node<T> prev = node;
		//当前索引index那个结点
		Node<T> cur = node;
		for (int i = 0; i < index; i++) {
			prev = cur;
			cur = cur.next;
		}
		//此时需要将该结点从链表中删除,同时将当前结点移动到链表的头部
		prev.next = cur.next;
		//注意,这里将查询的结点拿出来后,链表长度要减1
		size--;
		//将查询的结点添加到链表头部
		putFirst(cur.data);
		return cur.data;
	}

	public static void main(String[] args) {
		LruLinkedList<Integer> lruLinkedList = new LruLinkedList<>(3);
		lruLinkedList.lruPutFirst(1);
		lruLinkedList.lruPutFirst(2);
		lruLinkedList.lruPutFirst(3);
		lruLinkedList.lruPutFirst(4);
		System.out.println("自定义lru集合长度为3,然后往lru缓存中添加结点,打印出lur集合中数据:" + lruLinkedList.toString() + "\n");
		lruLinkedList.lruGet(2);
		System.out.println("查询lru集合中索引为2的结点,然后打印出查询后的集合中元素的排序:" + lruLinkedList.toString());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值