方法:哈希表+双向链表知识点1:哈希表哈希表是一种根据关键码(key)直接访问值(value)的一种数据结构。而这种直接访问意味着只要知道key就能在O(1)O(1)O(1)时间内得到value,因此哈希表常用来统计频率、快速检验某个元素是否出现过等。知识点2:双向链表双向链表是一种特殊的链表,它除了链表具有的每个节点指向后一个节点的指针外,还拥有一个每个节点指向前一个节点的指针,因此它可以任意向前或者向后访问,每次更改节点连接状态的时候,需要变动两个指针。思路:插入与访问值都是O(1)O(1)O(1),没有任何一种数据结构可以直接做到。于是我们可以想到数据结构的组合:访问O(1)O(1)O(1)很容易想到了哈希表;插入O(1)O(1)O(1)的数据结构有很多,但是如果访问到了这个地方再选择插入,且超出长度要在O(1)O(1)O(1)之内删除,我们可以想到用链表,可以用哈希表的key值对应链表的节点,完成直接访问。但是我们还需要把每次访问的key值节点加入链表头,同时删掉链表尾,所以选择双向链表,便于删除与移动。于是我们的方法就是哈希表+双向链表。具体做法:step 1:构建一个双向链表的类,记录key值与val值,同时一前一后两个指针。用哈希表存储key值和链表节点,这样我们可以根据key值在哈希表中直接锁定链表节点,从而实现在链表中直接访问,能够做到O(1)O(1)O(1)时间访问链表任意节点。    //设置双向链表结构    class Node{         int key;        int val;        Node pre;        Node next;        //初始化        public Node(int key, int val) {            this.key = key;            this.val = val;            this.pre = null;            this.next = null;        }    }step 2:设置全局变量,记录双向链表的头、尾及LRU剩余的大小,并全部初始化,首尾相互连接好。//构建初始化连接//链表剩余大小this.size = k;this.head.next = this.tail;this.tail.pre = this.head;step 3:遍历函数的操作数组,检查第一个元素判断是属于set操作还是get操作。step 4:如果是set操作,即将key值与val值加入链表,我们先检查链表中是否有这个key值,可以通过哈希表检查出,如果有直接通过哈希表访问链表的相应节点,修改val值,并将访问过的节点移到表头;如果没有则需要新建节点加到表头,同时哈希表中增加相应key值(当然,还需要检查链表长度还有无剩余,若是没有剩余则需要删去链表尾)。//没有见过这个key,新值加入if(!mp.containsKey(key)){     Node node = new Node(key, val);    mp.put(key, node);    //超出大小,移除最后一个    if(size <= 0)         removeLast();    //大小还有剩余    else         //大小减1        size--;     //加到链表头    insertFirst(node); }//哈希表中已经有了,即链表里也已经有了else{      mp.get(key).val = val;    //访问过后,移到表头    moveToHead(mp.get(key)); }step 5:不管是新节点,还是访问过的节点都需要加到表头,若是访问过的,需要断开原来的连接,再插入表头head的后面。//移到表头函数void moveToHead(Node node){     //已经到了表头    if(node.pre == head)          return;    //将节点断开,取出来    node.pre.next = node.next;    node.next.pre = node.pre;    //插入第一个前面    insertFirst(node);}step 6:删除链表尾需要断掉尾节点前的连接,同时哈希表中去掉这个key值。void removeLast(){     //哈希表去掉key    mp.remove(tail.pre.key);    //断连该节点    tail.pre.pre.next = tail;     tail.pre = tail.pre.pre;}step 7:如果是get操作,检验哈希表中有无这个key值,如果没有说明链表中也没有,返回-1,否则可以根据哈希表直接锁定链表中的位置进行访问,然后重复step 5,访问过的节点加入表头。if(mp.containsKey(key)){    Node node = mp.get(key);    res = node.val;    moveToHead(node);}图示:Java代码实现:import java.util.*;public class Solution {    //设置双向链表结构    static class Node{         int key;        int val;        Node pre;        Node next;        //初始化        public Node(int key, int val) {            this.key = key;            this.val = val;            this.pre = null;            this.next = null;        }    }        //哈希表    private Map<Integer, Node> mp = new HashMap<>();    //设置一个头    private Node head = new Node(-1, -1);     //设置一个尾    private Node tail = new Node(-1, -1);     private int size = 0;         public int[] LRU (int[][] operators, int k) {        //构建初始化连接        //链表剩余大小        this.size = k;        this.head.next = this.tail;        this.tail.pre = this.head;        //获取操作数        int len = (int)Arrays.stream(operators).filter(x -> x[0] == 2).count();        int[] res = new int[len];        //遍历所有操作        for(int i = 0, j = 0; i < operators.length; i++){            if(operators[i][0] == 1)                //set操作                set(operators[i][1], operators[i][2]);            else                //get操作                res[j++] = get(operators[i][1]);        }        return res;    }         //插入函数    private void set(int key, int val){        //没有见过这个key,新值加入        if(!mp.containsKey(key)){             Node node = new Node(key, val);            mp.put(key, node);            //超出大小,移除最后一个            if(size <= 0)                 removeLast();            //大小还有剩余            else                 //大小减1                size--;             //加到链表头            insertFirst(node);         }        //哈希表中已经有了,即链表里也已经有了        else{              mp.get(key).val = val;            //访问过后,移到表头            moveToHead(mp.get(key));         }    }        //获取数据函数    private int get(int key){        int res = -1;        if(mp.containsKey(key)){            Node node = mp.get(key);            res = node.val;            moveToHead(node);        }        return res;    }    //移到表头函数    private void moveToHead(Node node){         //已经到了表头        if(node.pre == head)              return;        //将节点断开,取出来        node.pre.next = node.next;        node.next.pre = node.pre;        //插入第一个前面        insertFirst(node);    }        //将节点插入表头函数    private void insertFirst(Node node){         node.pre = head;        node.next = head.next;        head.next.pre = node;        head.next = node;    }        //删去表尾函数,最近最少使用    private void removeLast(){         //哈希表去掉key        mp.remove(tail.pre.key);        //断连该节点        tail.pre.pre.next = tail;         tail.pre = tail.pre.pre;    }}
点赞 0
评论 0
全部评论

相关推荐

06-27 12:54
已编辑
门头沟学院 Java
累了,讲讲我的大学经历吧,目前在家待业。我是一个二本院校软件工程专业。最开始选专业是觉得计算机感兴趣,所以选择了他。本人学习计算机是从大二暑假结束开始的,也就是大三开始。当时每天学习,我个人认为Java以及是我生活的一部分了,就这样持续学习了一年半,来到了大四上学期末,大概是在12月中旬,我终于找的到了一家上海中厂的实习,但我发现实习生的工作很枯燥,公司分配的活也不多,大多时间也是自己在自学。就这样我秋招末才找到实习。时间来到了3月中旬,公司说我可以转正,但是转正工资只有7000,不过很稳定,不加班,双休,因为要回学校参加答辩了,同时当时也是心高气傲,认为可以找到更好的,所以放弃了转正机会,回学校准备论文。准备论文期间就也没有投递简历。然后时间来到了5月中旬,这时春招基本也结束了,然后我开始投递简历,期间只是约到了几家下场面试。工资也只有6-7k,到现在我不知道该怎么办了。已经没有当初学习的心劲了,好累呀,但是又不知道该干什么去。在家就是打游戏,boss简历投一投。每天日重一次。26秋招都说是针对26届的人,25怎么办。我好绝望。要不要参加考公、考研、央国企这些的。有没有大佬可以帮帮我。为什么感觉别人找工作都是顺其自然的事情,我感觉自己每一步都在艰难追赶。八股文背了又忘背了又忘,我每次都花很长时间去理解他,可是现在感觉八股、项目都忘完了。真的已经没有力气再去学习了。图片是我的简历,有没有大哥可以指正一下,或者说我应该走哪条路,有点不想在找工作了。
码客明:太累了就休息一下兄弟,人生不会完蛋的
如果实习可以转正,你会不...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务