单链表:插入O(1)、访问O(n)
循环链表:约瑟夫问题
双向链表:插入、删除等操作更加简单高效,如删除指定结点之前的结点
数组vs链表
- 数组可以借助CPU缓存机制预读数据,访问效率更高。缺点是大小固定,若过大,系统可能无法分配足够空间,而链表没有大小限制,天然支持动态扩容。(区别于ArrayList的动态扩容,没有空间时,会申请一个更大的空间,把数据拷贝进去,非常耗时)
- 链表消耗内存大,频繁插入、删除操作容易造成内存碎片,导致频繁的垃圾回收。
缓存淘汰策略:
FIFO:先进先出策略
LFU:最少使用策略
LRU:最近最少使用策略
Q1:用链表实现LRU?
思路:维护一个有序链表,越靠近尾部的是越早之前访问的。当有一个新的数据被访问时,从链表头开始顺序遍历链表。1、如果这个数据在此之前已经缓存在链表中了,则将其从原来的位置删除,并插入到链表头部。2、如果该数据没有缓存在链表中,分两种情况:若缓存未满,则直接插入链表头部;若缓存已满,则删除尾结点,将新的数据插入链表的头部。
package bishi360;
import java.util.Scanner;
public class LRUBasedLinkedList<T> {
//结点类
class SNode<T>{
private T element;
private SNode next;
public SNode(T element){
this.element = element;
}
public SNode(T element,SNode next){
this.element = element;
this.next = next;
}
public SNode(){
this.next=null;
}
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
public SNode getNext() {
return next;
}
public void setNext(SNode next) {
this.next = next;
}
}
//设置默认容量
private final static Integer DEFAULT_CAPACITY = 10;
//头节点
private SNode<T> headNode;
//链表长度
private Integer length;
//链表容量
private Integer capacity;
public LRUBasedLinkedList(){
this.headNode = new SNode<>();
this.capacity = DEFAULT_CAPACITY;
this.length = 0;
}
public LRUBasedLinkedList(int capacity){
this.headNode = new SNode<>();
this.capacity = capacity;
this.length = 0;
}
//添加最新访问结点add
public void add(T data){
//查找是否已经存在(找到他的前一个结点,则存在)
SNode preNode = findPreNode(data);
//链表中存在,则删除原数据,再插入链表头部
if(preNode!=null){
deleteNextElement(preNode);
insertAtBegin(data);
}else{ //不存在,若容量满了则删除尾结点,否则直接插入头部
if(length>= this.capacity){
deleteLastElement();
}
insertAtBegin(data);
}
}
//获取查找元素的前一个结点findPreNode
public SNode findPreNode(T data){
SNode node = headNode;
while (node.getNext()!= null){
if(data.equals(node.getNext().getElement())){
return node;
}
node=node.getNext();
}
return null;
}
//删除PreNode结点后一个元素(即所查找的元素)deleteNextElement
public void deleteNextElement(SNode preNode){
SNode temp = preNode.getNext();
preNode.setNext(temp.getNext());
temp = null;
length--;
}
//链表头部插入结点insertAtBegin
public void insertAtBegin(T data){
SNode next = headNode.getNext();
headNode.setNext(new SNode(data, next));
length++;
}
//删除链表尾部节点deleteLastElement
private void deleteLastElement(){
SNode h = headNode;
//空链表直接返回
if(h.getNext()==null){
return;
}
//获取倒数第二个结点
while(h.getNext().getNext() !=null){
h= h.getNext();
}
SNode temp = h.getNext();
h.setNext(null);
temp = null;
length--;
}
//遍历链表
private void printAll(){
SNode node = headNode.getNext();
while (node!=null){
System.out.print(node.getElement()+",");
node = node.getNext();
}
}
//main函数
public static void main(String[] args) {
LRUBasedLinkedList list = new LRUBasedLinkedList();
Scanner sc = new Scanner(System.in);
while (true) {
list.add(sc.nextInt());
list.printAll();
}
}
}