题解LRU算法

LRU算法,最久未被使用算法,有一个固定大小的cache,当装不下任务的时候,就会选取,最久未被使用过的任务,进行淘汰。对于LRU要求有如下:
1,查找和删除都是O1级别的,O1级别立马就想到了hash表结构,满足这个要求。
2,要求把使用过的任务安放起来,用的时候,就去寻找,这个想到了用链表来实现。
实际上java中提供了这两种结构的结合题,这就是LinkedHashMap。用这种结构就能解决力扣上的LRU算法题,代码如下:

class LRUCache {

        private int cap;
	private Map<Integer, Integer> map = new LinkedHashMap<>();  // 保持插入顺序

	public LRUCache(int capacity) {
		this.cap = capacity;
	}

	public int get(int key) {
		if (map.keySet().contains(key)) {
			int value = map.get(key);
			map.remove(key);
                       // 保证每次查询后,都在末尾
			map.put(key, value);
			return value;
		}
		return -1;
	}

	public void put(int key, int value) {
		if (map.keySet().contains(key)) {
			map.remove(key);
		} else if (map.size() == cap) {
			Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
			iterator.next();
			iterator.remove();

			// int firstKey = map.e***ySet().iterator().next().getValue();
			// map.remove(firstKey);
		}
		map.put(key, value);
	}
}

但是一般来说,面试考察的就是你怎么去构建哪两种结构,一般是不会允许这样的,那么就需要手动的创建链表,和hash

  • 首先创建链表中的每个元素节点,节点中的属性有,key,value,pre(指向前一个节点),next(指向后一个节点)。
class Node{
      int key;
      int value;
      Node pre;
      Node next;
      public Node(int key,int value){
       this.key=key;
       this.value=value;
      }
      public Node(){
      }
}
  • 创建双向链表,双向链表里面的属性就少一些,是头节点,和尾节点
class DoubleList{//双链表head<->[key,val]<->tail
   //头节点
   Node head;
   //尾节点
   Node tail;
   public DoubleList(){//构造方法
       head=new Node(0,0);
       tail=new Node(0,0);
       head.next=tail;
       tail.pre=head;
   }
   public void addFirst(Node n){//头插法
     head.next.pre=n;
     n.next=head.next;
     head.next=n;
     n.pre=head;
   }
   //删除某个节点
   public void remove(Node n){
       n.pre.next=n.next;
       n.next.pre=n.pre;
       n.next=null;
       n.pre=null;
   }
   public Node removeLast(){
       //删除最后一个并且返回
       Node res=tail.pre;
       remove(res);
       return res;
   }
}
  • 接下来就是创建put 和get方法,思路是这样的,无论是put还是get后,都相当于操作了这个节点,所以一定要放在链表的头部。对于get方法,通过map可以快速的查到链表里是否含有这个节点,如果含有,就把他获取,删除,在次添加。对于set方法,如果已经有key相同的情况,就修改它的value。删除,放入队头。还要检测是否大于cache,如果大于,就把链表的最后一个删除。实现代码如下:
class LRUCache {
    HashMap<Integer,Node> map;
    DoubleList cache;
    int cap;//容量
    public LRUCache(int capacity) {
        map = new HashMap<>();
        cache = new DoubleList();
        this.cap = capacity;
    }
    
    public int get(int key) {
        if(!map.containsKey(key))//若该节点不存在
            return -1;
        Node res = map.get(key);
        cache.remove(res);
        cache.addFirst(res);
        return res.val;
    }
    
    public void put(int key, int value) {
        Node n = new Node(key,value);
        if(map.containsKey(key)){//若该节点已经存在
            cache.remove(map.get(key));
        }else if(map.size()==cap){//若该节点不存在,但是cache已满
            Node last = cache.removeLast();
            map.remove(last.key);
        }
        cache.addFirst(n);
        map.put(key,n);
    }
}

如有错误请指出

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,演示了 Linux 内核中 LRU(Least Recently Used)页面置换算法的实现: ```c #include <stdio.h> #include <stdlib.h> #define CACHE_SIZE 4 typedef struct { int page; int counter; } Page; Page cache[CACHE_SIZE]; void initialize() { for (int i = 0; i < CACHE_SIZE; i++) { cache[i].page = -1; cache[i].counter = 0; } } int getLRUPage() { int minCounter = cache[0].counter; int lruIndex = 0; for (int i = 1; i < CACHE_SIZE; i++) { if (cache[i].counter < minCounter) { minCounter = cache[i].counter; lruIndex = i; } } return lruIndex; } void updateCache(int page) { for (int i = 0; i < CACHE_SIZE; i++) { if (cache[i].page == page) { cache[i].counter = 0; } else { cache[i].counter++; } } } void displayCache() { printf("Cache: "); for (int i = 0; i < CACHE_SIZE; i++) { if (cache[i].page == -1) { printf("- "); } else { printf("%d ", cache[i].page); } } printf("\n"); } void simulateLRU(int pages[], int numPages) { initialize(); printf("Simulating LRU Page Replacement Algorithm\n"); for (int i = 0; i < numPages; i++) { int currentPage = pages[i]; if (currentPage >= 0 && currentPage <= 9) { printf("Accessing Page: %d\n", currentPage); updateCache(currentPage); displayCache(); } else { printf("Invalid Page Number: %d\n", currentPage); } } } int main() { int pages[] = {1, 2, 3, 4, 1, 5, 6, 2, 7, 8, 7, 9, 3, 5}; int numPages = sizeof(pages) / sizeof(pages[0]); simulateLRU(pages, numPages); return 0; } ``` 这段代码模拟了一个简单的页面访问序列,并使用 LRU 页面置换算法更新缓存。其中,`CACHE_SIZE` 定义了缓存的大小,默认为4。`initialize` 函数初始化缓存,`getLRUPage` 函数找到最近最久未使用的页面索引,`updateCache` 函数根据访问情况更新缓存计数器,`displayCache` 函数打印当前缓存状态。`simulateLRU` 函数模拟了页面访问序列,并在每次访问后展示缓存状态。 请注意,这只是一个简单的示例代码,实际的 Linux 内核中的 LRU 页面置换算法更加复杂和高效。这里提供的代码仅用于演示基本概念和算法原理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值