146.LRU缓存算法 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制 。
1 2 3 4 5 6 7 8 9 10 11 实现 LRUCache 类: LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。 void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。 进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作? 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/lru-cache 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 
利用链表保持时序,利用hash存储节点 调试了好久,终于搞定了,需要仔细
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 class ListNode {   constructor(key, val, pre=null, next=null){     this.key = key;     this.val = val;     this.pre = pre;     this.next = next;   } } var LRUCache = function (capacity) {   // 最大长度   this.capacity = capacity   this.hash = new Map()   // 计数器   this.count = 0   // 虚拟头部   this.dummyHead = new ListNode(-1, -1)   // 虚拟尾部   this.dummyTail = new ListNode(-1, -1)   this.dummyHead.next = this.dummyTail   this.dummyTail.pre = this.dummyHead   // 头部   this.listHead = this.dummyHead   // 尾部   this.listTail = this.dummyTail }; LRUCache.prototype.get = function(key) {   if (this.hash.has(key)) {     const existNode = this.hash.get(key)     // 从原位置删除     existNode.next.pre = existNode.pre     existNode.pre.next = existNode.next     // 把节点插入虚拟尾部之前     this.insertBeforeTail(existNode)     return this.hash.get(key).val   }else{     return -1   } }; LRUCache.prototype.put = function(key, value) {   if (this.hash.has(key)) { // 已存在     const existNode = this.hash.get(key)     // 从原位置删除     existNode.next.pre = existNode.pre     existNode.pre.next = existNode.next     // 题目有个变更要求     existNode.val = value     // 把节点插入虚拟尾部之前     this.insertBeforeTail(existNode)   } else { // 未存在     const newNode = new ListNode(key, value)     if (this.count < this.capacity) { // 容量没满       // 把新节点插入虚拟尾部之前       this.insertBeforeTail(newNode)       // 用key记录节点       this.hash.set(key, newNode)       // 计数       this.count++     } else { // 容量满了       // 删除节点记录       this.hash.delete(this.listHead.next.key)       // 把虚拟头部后边节点删除       this.listHead.next = this.listHead.next.next       this.listHead.next.pre = this.listHead        // 把新节点插入虚拟尾部之前          this.insertBeforeTail(newNode)       // 用key记录节点       this.hash.set(key, newNode)     }   } }; LRUCache.prototype.insertBeforeTail = function (newNode) {   // 把节点插入虚拟尾部之前   this.listTail.pre.next = newNode   newNode.pre = this.listTail.pre   newNode.next = this.listTail   this.listTail.pre = newNode } 
利用map数据结构 利用map特别简单
map数据结构兼顾有序性和hash 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 var LRUCache = function(capacity) {     this.capacity = capacity;     this.map = new Map(); }; LRUCache.prototype.get = function(key) {     if(this.map.has(key)){         let temp=this.map.get(key)          this.map.delete(key);          this.map.set(key, temp);          return temp     }else{         return -1     } }; LRUCache.prototype.put = function(key, value) {     if(this.map.has(key)){         this.map.delete(key);     }     this.map.set(key,value);     if(this.map.size > this.capacity){               this.map.delete(this.map.keys().next().value);     } }; 
实现RandomizedSet 类:
RandomizedSet() 初始化 RandomizedSet 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 示例: 输入 ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] 输出 [null, true, false, true, 2, true, false, 2] 解释 RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。 randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。 randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。 randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。 randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。 randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。 randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/insert-delete-getrandom-o1 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 
利用map进行设计,利用水塘抽样算法实现等概率 但是随机获取 时间复杂度不是O1,而是O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 var RandomizedSet = function() {   this.hash = new Map() }; RandomizedSet.prototype.insert = function(val) {   if (this.hash.has(val)) {     return false   } else {     this.hash.set(val, true)     return true   } }; RandomizedSet.prototype.remove = function(val) {   if (this.hash.has(val)) {     this.hash.delete(val)     return true   } else {     return false   } }; RandomizedSet.prototype.getRandom = function() {   let count = 1;   let iterator = this.hash.keys();   let result = null;   while (true) {     const { value, done } = iterator.next()     if (done) {       break;     }     if (Math.floor(Math.random() * count) === 0) {       result = value     }     count++   }   return result }; 
利用hash实现O1存储删除,数组实现O1等概率访问 难点还是在于理解和设计,熟悉数据结构的基础方法时间复杂度
删除的前提是访问
数组某个元素和尾部交换后删除,O1
数组push 01
代码重点在于交换时,hash对应的index也需要考虑
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 var RandomizedSet = function() {   this.hash = Object.create(null, {})   this.array = [] }; RandomizedSet.prototype.insert = function(val) {   if (typeof this.hash[val] !== "undefined") {     return false   } else {     this.array.push(val)     // 存储位置     this.hash[val] = this.array.length - 1     return true   } }; RandomizedSet.prototype.remove = function(val) {   if (typeof this.hash[val] !== "undefined") {     // 待删除元素的索引     const index = this.hash[val]     // 对末尾元素进行操作     const tailValue = this.array[this.array.length - 1]     this.hash[tailValue] = index     this.array[index] = tailValue     // 删除     this.array.pop()     delete this.hash[val]     return true   } else {     return false   } }; RandomizedSet.prototype.getRandom = function() {   return this.array[Math.floor(Math.random()*this.array.length)] }; 
232.用栈实现队列 题解简单易懂 ,主要是需要搞懂push pop穿插如果保证有序性就可以了,pop必须一次性从inStack全部转移过来才能保持顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 var MyQueue = function() {   this.inStack = []   this.outStack = [] }; MyQueue.prototype.push = function(x) {   this.inStack.push(x) }; MyQueue.prototype.pop = function() {   if(!this.outStack.length){     while(this.inStack.length){       this.outStack.push(         this.inStack.pop()       )     }   }   return this.outStack.pop() }; MyQueue.prototype.peek = function() {   if(!this.outStack.length){     while(this.inStack.length){       this.outStack.push(         this.inStack.pop()       )     }   }   return this.outStack[this.outStack.length-1] }; MyQueue.prototype.empty = function() {   return !this.inStack.length && !this.outStack.length }; 
请你仅使用两个队列 实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var MyStack = function() {   this.list = []   this.tempList = [] }; MyStack.prototype.push = function(x) {   if(this.list.length){     while(this.list.length){       this.tempList.push(this.list.shift())     }     this.list.push(x)     while(this.tempList.length){       this.list.push(this.tempList.shift())     }   }else{     this.list.push(x)   } }; MyStack.prototype.pop = function() {   return this.list.shift() }; MyStack.prototype.top = function() {   return this.list[0] }; MyStack.prototype.empty = function() {   return !this.list.length };