左神算法笔记(二十一)——LFU缓存算法实现

LFU(Least Frequently Used):淘汰最近访问频率最小的元素。

思路

设计思路:两个双向链表,横向的双向链表用来统计访问频率,纵向的双向链表,没出现过的元素出现在横向链表的头结点上,如果一直增加没有访问,则将元素全部挂到头结点的下方,纵向形成双向链表。
横向链表中统计访问的频率,如果增加某个节点的访问一次,则横向链表判断访问总次数是否存在,如果存在,则将节点放到该次数节点的下方,如果不存在则新建一个头结点,将该节点挂到下面。如果有一个次数下方不再保存节点,则将该次数在横向链表中删去。

代码

public static class Node{
	public Integer key;
	public Integer value;
	public Integer times;
	public Node up;
	public Node down;

	public Node(int key,int value,int times){
		this.key = key;
		this.value = value;
		this.times = times;
	}
}

public static class LFUCache{
	//定义了头链表的整体结构,中间存放的内容为node类型,同时存在前后。
	public static class NodeList{
		public Node head;
		public Node tail;
		public NodeList last;
		public NodeList next;

		public NodeList(Node node){
			head = node;
			tail = node;
		}

		public void addNodeFromHead(Node newHead){
			newHead.down = head;
			head.up = newHead;
			head = newHead;
		}

		public boolean isEmpty(){
			return head ==null;
		}
		//删掉任何一个节点
		public void deleteNode(Node node){
			if(head == tail){
				head =null;
				tail = null;
			}else{
				if(node == head){
					head = node.down;
					head.up = null;
				}else if(node == tail){
					tail = node.up;
					tail.down = null;
				}else{
					node.up.down = node.down;
					node.down.up = node.up;
				}
			}
			node.up = null;
			node.down = null;
		}
	}
	//容量
	private int capacity;
	private int size;
	//key->node类型,不是次数
	private HashMap<Integer,Node> records;
	//对于任何node,都可以查到他属于哪个NodeList
	private HashMap<Node,NodeList> heads;
	//大结构是nodeList串起来的,现在给出双向链表的头部方便查找
	private NodeList headList;

	public LFUCache(int capacity){
		this.capacity = capacity;
		this.size = 0;
		this.records = new HashMap<>();
		this.heads = new HashMap<>();
		headList = null;
	}

	public void set(int key,int value){
		if(records.containsKey(key)){
			Node node = records.get(key);
			node.value = value;
			node.times++;
			//找到新set的值将当前的词频拿出来挂到新的词频上
			NodeList curNodeList = heads.get(node);
			move(node,curNodeList);
		}else{
			//如果此时已经到了阈值,则此时就需要进行数据删除,再放入新的值
			if(size == capacity){
				Node node = headList.tail;
				headList.deleteNode(node);
				//如果发现当前headlist内没有任何内容,则此时需要将当前词频删除。
				modifyHeadList(headList);
				records.remove(node.key);
				heads.remove(node);
				size--;
			}
			Node node = new Node(key,value,1);
			//如果整个链表现在是空的,则新建一个nodelist
			if(headList == null){
				headList = new NodeList(node);
			}else{
				//最少的词频的times进行判断,
				if(headList.head.times.equals(node.times)){
					headList.addNodeFromHead(node);
				}else{
					//新建一个词频为1的链表,并且需要换头链表
					NodeList newList = new NodeList(node);
					newList.next = headList;
					HeadList.last = newList;
					headList = newList;
				}
			}
			records.put(key,node);
			heads.put(node.headList);
			size++;
		}
	}

	private void move(Node node,Nodelist oldNodeList){
		oldNodelist.deleteNode(node);
		//判断拿掉节点之后该链表是否为空,如果为空,则前一个链表是现在老链表的前一个,如果老链表自己还有内容,则新链表的前一个是老链表
		NodeList preList = modifyHeadList(oldNodeList) ? oldNodeList.last : oldeNodeList;
		//nextList是老链表的下一个list
		NodeList nextList = oldNodeList.next;
		//如果当前词频已经是最高了,则此时新建一个,直接跟前方的相连
		if(nextList == null){
			NodeList newList = new NodeList(node);
			if(preList != null){
				preList.next = newList;
			}
			newList.last = preList;
			if(headList == null){
				headList = newList;
			}
			head.put(node,newList);
		}else{
			//如果正好大一,则直接放到nextList上	
			if(nextList.head.times.equals(node.times)){
				nextList.addNodeFromHead(node);
				head.put(node,nextList);
			}else{
				//不是的话则新建一个链表
				NodeList newList = new NodeList(node);
				if(preList != null){
					preList.next = newList;
				}
				newList.last = preList;
				newList.next = nextList;
				nextList.last = newList;
				if(headList == nextList){
					HeadList = newList;
				}
				head.put(node,newList);
			}
		}
	}

	//当链表中数据被拿出时,判断是否需要删除整个list
	private boolean modifyHeadList(NodeList nodeList){
		if(nodeList.isEmpty()){
		//如果当前list时headList的话
			if(headList == nodeList){
				headList = nodeList.next;
				if(headList != null){
					headList.last = null;
				}
			}else{
			//如果不是头的话,将中间该链表删除,形成新的链表
				nodeList.last.next = nodeList.next;
				if(nodeList.next != null){
					nodeList.next.last = nodeList.last;
				}
			}
			return true;
		}
		return false;
	}

	public int get(int key){
		if(!records.containsKey(key)){
			return -1;//不存在
		}
		Node node = records.get(key);
		node.times++;
		NodeList curNodeList = heads.get(node);
		move(node,curNodeList);
		return node.value;
	}
}			

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值