2022-5-24 笔试真题练习
手写LRU:
1 import java.util.HashMap; 2 import java.util.Map; 3 4 public class LRUCache { 5 Map<Integer,Node> map=new HashMap<>(); 6 Node head; 7 Node tail; 8 int size; 9 int capacity; 10 static class Node{ 11 int key,value; 12 Node prev,next; 13 Node(){ 14 } 15 Node(int k,int v){ 16 key=k; 17 value=v; 18 } 19 } 20 21 22 // public void getfirst(int key){ 23 // Node node=map.get(key); 24 // removeKey(node); 25 // addfirst(node); 26 // } 27 28 public void removeNode(Node node){ 29 Node prev=node.prev; 30 Node next=node.next; 31 prev.next=next; 32 next.prev=prev; 33 map.remove(node.key); 34 size--; 35 } 36 37 public void addNode(Node node){ 38 head.next.prev=node; 39 node.next=head.next; 40 head.next=node; 41 node.prev=head; 42 map.put(node.key,node); 43 size++; 44 } 45 // prev->node->tail 46 // prev<-node<-tail 47 // public void removeLast(){ 48 // Node node=tail.prev; 49 // tail.prev=node.prev; 50 // tail.prev.next=tail; 51 // map.remove(node.key); 52 // size--; 53 // } 54 public LRUCache(int capacity) { 55 head=new Node(); 56 tail=new Node(); 57 head.next=tail; 58 tail.prev=head; 59 size=0; 60 this.capacity=capacity; 61 } 62 63 public int get(int key) { 64 if (!map.containsKey(key)){ 65 return -1; 66 }else{ 67 // 提取当前key到最前面 68 Node node=map.get(key); 69 removeNode(node); 70 addNode(node); 71 return map.get(key).value; 72 } 73 74 } 75 76 public void put(int key, int value) { 77 if (map.containsKey(key)){ 78 Node node=map.get(key); 79 node.value=value; 80 removeNode(node); 81 addNode(node); 82 }else{ 83 Node node=new Node(key,value); 84 if (size==capacity) { 85 removeNode(tail.prev); 86 } 87 addNode(node); 88 } 89 } 90 91 92 }
思路:map记录映射位置,双向链表记录顺序。
手写LFU:
1 import java.util.HashMap; 2 import java.util.Map; 3 import java.util.PriorityQueue; 4 5 public class LFUCache { 6 Map<Integer,Node> keyToNode; 7 Map<Integer,DoubleList> freqToList; 8 int capacity,minFreq; 9 public LFUCache(int capacity) { 10 keyToNode=new HashMap<>(); 11 freqToList=new HashMap<>(); 12 this.capacity=capacity; 13 minFreq=0; 14 } 15 16 public int get(int key) { 17 if (keyToNode.size()==0||!keyToNode.containsKey(key)) return -1; 18 // freq要加1 添加到freq+1的链表中 更新最小频率 19 Node node= keyToNode.get(key); 20 int freq=node.freq; 21 freqToList.get(freq).deleteNode(node); 22 DoubleList doubleList=freqToList.getOrDefault(++node.freq,new DoubleList()); 23 doubleList.addNode(node); 24 freqToList.put(node.freq,doubleList); 25 if (freqToList.getOrDefault(minFreq,new DoubleList()).size==0){ 26 minFreq++; 27 } 28 return node.value; 29 } 30 31 public void put(int key, int value) { 32 if (!keyToNode.containsKey(key)){ 33 // 添加key的索引,对应的链表也要加, minfreq更新 34 Node node=new Node(key,value,1); 35 if (keyToNode.size()==capacity){ 36 // 满了 先去掉一个 37 DoubleList doubleList=freqToList.get(minFreq); 38 Node needDelete=doubleList.head.next; 39 keyToNode.remove(needDelete.key); 40 doubleList.deleteNode(doubleList.head.next); 41 } 42 keyToNode.put(key,node); 43 DoubleList doubleList=freqToList.getOrDefault(1,new DoubleList()); 44 doubleList.addNode(node); 45 freqToList.put(1,doubleList); 46 // 必定为1 47 minFreq=1; 48 }else{ 49 // 取出 50 // 从freq中删掉,加入到freq+1中 更新minfreq 51 Node node=keyToNode.get(key); 52 freqToList.get(node.freq).deleteNode(node); 53 DoubleList doubleList=freqToList.getOrDefault(++node.freq,new DoubleList()); 54 doubleList.addNode(node); 55 freqToList.put(node.freq,doubleList); 56 if (freqToList.getOrDefault(minFreq,new DoubleList()).size==0){ 57 minFreq++; 58 } 59 node.value=value; 60 } 61 } 62 } 63 64 class Node{ 65 int key,value,freq; 66 Node prev,next; 67 Node(){ 68 69 } 70 Node(int k,int v,int f){ 71 key=k; 72 value=v; 73 freq=f; 74 } 75 } 76 77 class DoubleList{ 78 int size; 79 Node head,tail; 80 DoubleList(){ 81 head=new Node(); 82 tail=new Node(); 83 head.next=tail; 84 tail.prev=head; 85 size=0; 86 } 87 //尾部加入 88 public void addNode(Node node){ 89 tail.prev.next=node; 90 node.prev=tail.prev; 91 node.next=tail; 92 tail.prev=node; 93 size++; 94 } 95 //删除节点,一般从头部删除 96 public void deleteNode(Node node){ 97 Node prev=node.prev; 98 Node next=node.next; 99 prev.next=next; 100 next.prev=prev; 101 size--; 102 } 103 104 }
思路:双哈希表,一个表记录位置,一个表记录频率对应的双向链表。
1 import java.util.*; 2 3 public class LFUCache2 { 4 Map<Integer,NewNode> map; 5 TreeSet<NewNode> set; 6 int capacity,time; 7 public LFUCache2(int capacity) { 8 map=new HashMap<>(); 9 set=new TreeSet<>(); 10 this.capacity=capacity; 11 time=0; 12 } 13 14 public int get(int key) { 15 if (capacity==0||!map.containsKey(key)) return -1; 16 NewNode node=map.get(key); 17 map.remove(key); 18 set.remove(node); 19 node.freq++; 20 node.time=++time; 21 map.put(key,node); 22 set.add(node); 23 return node.value; 24 } 25 26 public void put(int key, int value) { 27 if (capacity==0) return; 28 if (!map.containsKey(key)){ 29 // 新增 30 if (capacity<=set.size()){ 31 NewNode node = set.first(); 32 map.remove(node.key); 33 NewNode newnode=new NewNode(key,value,++time,1); 34 set.remove(node); 35 map.put(key,newnode); 36 set.add(newnode); 37 }else{ 38 NewNode newnode=new NewNode(key,value,++time,1); 39 set.add(newnode); 40 map.put(key,newnode); 41 } 42 }else{ 43 // 更新 44 NewNode node = map.get(key); 45 map.remove(key); 46 set.remove(node); 47 node.freq++; 48 node.time=++time; 49 node.value=value; 50 map.put(key,node); 51 set.add(node); 52 } 53 } 54 55 public static void main(String[] args) { 56 LFUCache2 lfu = new LFUCache2(2); 57 lfu.put(2, 1); // cache=[1,_], cnt(1)=1 58 lfu.put(3, 2); // cache=[2,1], cnt(2)=1, cnt(1)=1 59 System.out.println(lfu.get(3)); 60 // cache=[1,2], cnt(2)=1, cnt(1)=2 61 //lfu.put(3, 3); // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小 62 // cache=[3,1], cnt(3)=1, cnt(1)=2 63 System.out.println(lfu.get(2)); 64 //System.out.println(lfu.get(3)); 65 // cache=[3,1], cnt(3)=2, cnt(1)=2 66 lfu.put(4, 3); // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用 67 // cache=[4,3], cnt(4)=1, cnt(3)=2 68 System.out.println(lfu.get(2)); 69 System.out.println(lfu.get(3)); 70 // cache=[3,4], cnt(4)=1, cnt(3)=3 71 System.out.println(lfu.get(4)); 72 } 73 } 74 75 class NewNode implements Comparable<NewNode> { 76 int key,value,time,freq; 77 NewNode(){ 78 79 } 80 NewNode(int k,int v,int t,int f){ 81 key=k; 82 value=v; 83 time=t; 84 freq=f; 85 } 86 87 @Override 88 public int compareTo(NewNode o) { 89 return this.freq==o.freq?this.time-o.time:this.freq-o.freq; 90 } 91 }
思路:treeset 集合去重并且根据频率和时间排序。
多多的魔术盒子
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
多多鸡有N个魔术盒子(编号1~N),其中编号为i的盒子里有i个球。
多多鸡让皮皮虾每次选择一个数字X(1 <= X <= N),多多鸡就会把球数量大于等于X个的盒子里的球减少X个。
通过观察,皮皮虾已经掌握了其中的奥秘,并且发现只要通过一定的操作顺序,可以用最少的次数将所有盒子里的球变没。
多多鸡让皮皮虾每次选择一个数字X(1 <= X <= N),多多鸡就会把球数量大于等于X个的盒子里的球减少X个。
通过观察,皮皮虾已经掌握了其中的奥秘,并且发现只要通过一定的操作顺序,可以用最少的次数将所有盒子里的球变没。
那么请问聪明的你,是否已经知道了应该如何操作呢?
输入描述:
第一行,有1个整数T,表示测试用例的组数。
(1 <= T <= 100)
接下来T行,每行1个整数N,表示有N个魔术盒子。
(1 <= N <= 1,000,000,000)
输出描述:
共T行,每行1个整数,表示要将所有盒子的球变没,最少需要进行多少次操作。
1 //5 1 2 3 4 5 2 // 1 2 0 1 2 3 // 2N+1 1 2 3 4 N 2N+1 4 // 1 2 3 4 N-1 0 N-1 N 4 3 2 1 5 // f(2*n+1)=f(n-1)+1 6 // 1 2 3 4 5 N .. 2N 7 // 1 2 3 4 5 N-1 0 1 2 N-1 N 8 // f(2*n)=max 9 // 1 2 3 4 5 6 10 // 1 2 0 1 2 3 11 // 1 0 0 1 0 1 12 import java.util.*; 13 public class Main{ 14 public static void main(String[] args) { 15 Scanner sc=new Scanner(System.in); 16 int n=sc.nextInt(); 17 for (int i=0;i<n;i++){ 18 System.out.println(f(sc.nextInt())); 19 } 20 } 21 public static int f(int x){ 22 if (x==1) return 1; 23 else if (x%2==0) return f(x/2)+1; 24 else return f((x-1)/2)+1; 25 } 26 }
思路:递归公式,
/**
* f(2n)=f(n)+1
* f(2*n+1)=f(n)+1
* n=1 1
* n=2 2
* n=3 2
* n=4 3
* n=5 f(3)+1 f(3)=f(2)+1 f(2)=2;
* // 1 2 3... n ... 2n-1
* 1 2 3 4..n-1.0 1 2 ...n-1
* 1 2 3 4 5
* 1 2 0 1 2
* 0 1 0 1 0
*
*/
多多的排列函数
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
数列 {An} 为N的一种排列。
例如N=3,可能的排列共6种:
1
2
3
4
5
6
|
1, 2, 3 1, 3, 2 2, 1, 3 2, 3, 1 3, 1, 2 3, 2, 1 |
定义函数F:
其中|X|表示X的绝对值。
现在多多鸡想知道,在所有可能的数列 {An} 中,F(N)的最小值和最大值分别是多少。
输入描述:
第一行输入1个整数T,表示测试用例的组数。
( 1 <= T <= 10 )
第二行开始,共T行,每行包含1个整数N,表示数列 {An} 的元素个数。
( 1 <= N <= 100,000 )
输出描述:
共T行,每行2个整数,分别表示F(N)最小值和最大值
1 import java.util.*; 2 public class Main{ 3 public static void main(String[] args) { 4 Scanner sc=new Scanner(System.in); 5 int n=sc.nextInt(); 6 for (int i=0;i<n;i++){ 7 int num=sc.nextInt(); 8 int min=(num%4==0||num%4==3)?0:1; 9 num--; 10 int max=(num%4==0||num%4==3)?num+1:num; 11 System.out.println(min+" "+max); 12 } 13 } 14 }
思路:找规律。n,n-1,n-2,n-3必定可以使最小值变为0,所以每四个一组可以抵消最小值的计算。所以按照4的余数分类。
而最大值可以根据上一组排列的最小值计算得出。
/**
* 4n+3 最小值为0
* 4n+2 最小值为1
* 4n+1 最小值为1
* 4n 最小值也为0
* 偶数
* 1 4 2 3
* min<=f(n-1)<=max
* n-max<=f(n)<=n-min
* max(n)=n-min(n-1)
* min 1 1 0 0 1 1
* max 1 1 2 4 5 5
*/