import java.util.*;


class Node {//节点(数据单元)
    int key ;
    int val ;
    int count ;//节点访问的次数
    Node(int key , int val) {
        this.key = key ;
        this.val = val ;
    }
}
class LFU {//缓存结构
    //根据key查询Node
    HashMap<Integer , Node> dic ;
    /*根据访问次数count查询List(list里面存储所有访问次数为count的节点,并且约靠近链尾的节点约久未访问)*/
    HashMap<Integer , LinkedList<Node>> tim ;
    int minTim ;//被访问最少的节点的 访问次数
    int maxSize ;//LFU缓存的容量
    int size ;//当前使用的容量
    LFU(int capcity) {
        this.dic = new HashMap<>() ;
        this.tim = new HashMap<>() ;
        this.minTim = 0 ;  
        this.maxSize = capcity ;
        this.size = 0 ;
    }
    //set(key,value)
    public void set(int key , int val) {
        if(dic.containsKey(key)) {//如果缓存中存在key
            Node find = dic.get(key) ;
            find.val = val ;//置换key所对应的value
            flush(find) ;//刷新一下访问记录,即让当前的访问次数加一,并且从原来的链表移动到新的链表
        } else {//如果缓存中不存在key
            Node newNode = new Node(key , val) ;//新建节点
            newNode.count = 1 ;//第一次访问
            if(this.size == this.maxSize) {//如果缓存满了
                removeByLFU() ;//置换        
            } else {//缓存未满,size加一
                this.size ++ ;
            }
            dic.put(key , newNode) ;//存数据
            if(tim.containsKey(1)) {//如果已经存在次数为1的链表
                tim.get(1).addFirst(newNode) ;//直接添加,在链头哦
            } else {//如果不存在次数为1的链表
                LinkedList<Node> newList = new LinkedList<>() ;//新建一个链表
                newList.add(newNode) ;//添加
                this.minTim = 1 ;//刷新最小访问次数
                tim.put(1 , newList) ;//将该链表以key为1,存入tim
            }
        }
    }
    //get(key)
    public int get(int key) {
        if(!dic.containsKey(key)) return -1 ;//不存在直接返回-1
        Node find = dic.get(key) ;//得到对应的节点
        flush(find) ;//刷新节点的访问记录
        return find.val ;
    }
    //刷新节点的访问记录
    public void flush(Node find) {
        LinkedList<Node> oldBelongsList = tim.get(find.count) ;//获取节点原来所在的链表
        oldBelongsList.remove(find) ;//从原来的链表中移除
        if(oldBelongsList.size() == 0) {//如果原来的链表已经没有数据了
            if(find.count == this.minTim) {//并且 当前节点就是最少访问节点,那么要更新最少访问节点的访问次数
                this.minTim ++ ;    
            }
            tim.remove(find.count) ;//把空链表移除
        }
        find.count ++ ;//当前节点的访问次数+1
        if(tim.containsKey(find.count)) {//如果新的访问次数岁对应的链表已经存在
            tim.get(find.count).addFirst(find) ;//则直接在新的链表中加入当前节点
        } else {//如果新的访问次数岁对应的链表不存在
            LinkedList<Node> newBelongsList = new LinkedList<>() ;//创建链表
            newBelongsList.add(find) ;//添加节点
            tim.put(find.count , newBelongsList) ;//将新的链表加入到tim中
        }
    }
    //(淘汰访问次数最少的节点,次数一样则淘汰最久未访问的节点)
    public void removeByLFU() {
        LinkedList<Node> minTimList = tim.get(this.minTim) ;//获取最少访问次数所对应的链表
        Node target = minTimList.get(minTimList.size()-1) ;//获取这个访问次数的"众多"节点中的最久未访问哪一个节点(即,链表尾部的那个节点)
        minTimList.remove(target) ;//删除那个节点
        if(minTimList.size() == 0) {//如果这个链表已经空了,则将其从tim中移除掉
            tim.remove(this.minTim) ;
        }
        dic.remove(target.key) ;//将节点从dic中移除
        //注意:这个方法中并没有更新size和minTim,因为客户在调用本方法后,必定会再增加一个节点
        //然后将minTim设置为1,(所以在本方法更新size和minTim多余,至少在这种实现的方式下是的,嘿嘿,才疏学浅,见谅!!)
    }
}
public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LFU (int[][] operators, int k) {
        int getCount = (int)(Arrays.stream(operators).filter(a->a[0] == 2).count()) ;
        int res[] = new int[getCount] ;
        int index = 0 ;
        LFU lfu = new LFU(k) ;
        for(int i = 0 ; i < operators.length ; i ++) {
            if(operators[i].length == 2) {
                res[index ++] = lfu.get(operators[i][1]) ;
            } else {
                lfu.set(operators[i][1] , operators[i][2]) ;
            }
        }
        return res ;
    }
}