一、LeetCode 146. LRU 缓存
解题思路:LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。这样以来,我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
对于 get 操作,首先判断 key 是否存在:
如果 key 不存在,则返回 -1−1;
如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
对于 put 操作,首先判断 key 是否存在:
如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
LRU缓存机制,即采用最近最少使用的缓存策略。它的原则是,如果一个数据最近没有被访问到,那么它将来被访问的几率也很小,也就是说当限定的内存空间已没有其他空间可用时,应该把最久没有访问到的数据去除掉。
classLRUCache {
// 定义双链表classDLinkNode{
int key;
int value;
DLinkNode pre;
DLinkNode next;
publicDLinkNode(){}
publicDLinkNode(int _key,int _value){
key=_key;
value=_value;
}
}
//定义哈希表、头指针、尾指针等全局变量private Map<Integer,DLinkNode>cache=newHashMap<Integer,DLinkNode>();
int size;
int capacity;
private DLinkNode head,tail;
//定义LRUCache函数体publicLRUCache(int capacity) {
this.size=0;
this.capacity=capacity;
head=newDLinkNode();
tail=newDLinkNode();
head.next=tail;
tail.pre=head;
}
publicintget(int key) {
DLinkNode node=cache.get(key);
if(node==null)
return -1;
//把查到的结点移到头结点上
moveToHead(node);
return node.value;
}
publicvoidput(int key, int value) {
//1、先查有没有这个结点
DLinkNode node=cache.get(key);
if(node==null){
DLinkNode temp=newDLinkNode(key,value);
cache.put(key,temp);
addToHead(temp);
++size;
if(size>capacity){
DLinkNode tailnode=deleteTail();
cache.remove(tailnode.key);
--size;
}
}
else{
node.value=value;
moveToHead(node);
}
}
//将结点插入至头结点后publicvoidaddToHead(DLinkNode node){
node.next=head.next;
head.next.pre=node;
node.pre=head;
head.next=node;
}
//把查到的结点移到头结点上publicvoidmoveToHead(DLinkNode node){
deleteNode(node);
addToHead(node);
}
voiddeleteNode(DLinkNode node){
node.pre.next=node.next;
node.next.pre=node.pre;
}
//删除尾结点public DLinkNode deleteTail(){
DLinkNode deleteres=tail.pre;
deleteNode(deleteres);
return deleteres;
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
二、剑指 Offer II 004. 只出现一次的数字
思路:
使用哈希映射统计数组中每个元素的出现次数。对于哈希映射中的每个键值对,键表示一个元素,值表示其出现的次数。在统计完成后,遍历哈希映射即可找出只出现一次的元素。
class Solution {
public int singleNumber(int[] nums) {
Map<Integer,Integer>num=new HashMap<Integer,Integer>();
for(int n:nums){
num.put(n,num.getOrDefault(n,0)+1);
}
for(Map.Entry<Integer,Integer>entry:num.entrySet()){
int e=entry.getKey(),count=entry.getValue();
if(count==1)
return e;
}
return 0;
}
}
三、两数之和
思路:用哈希表
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer>m=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
m.put(nums[i],m.getOrDefault(target-nums[i],i));
if(m.get(nums[i])!=i)
return new int[]{m.get(target-nums[i]),i};
}
return new int[]{-1,-1};
}
}
四、数组能形成多少数对
思路:使用哈希表以及getOrDefault函数
class Solution {
public int[] numberOfPairs(int[] nums) {
Map<Integer,Boolean>finder=new HashMap<Integer,Boolean>();
int res=0;
for(int n:nums){
finder.put(n,!finder.getOrDefault(n,false));
if(!finder.get(n))
res++;
}
return new int[]{res,nums.length-2*res};
}
}
五、剑指 Offer 39. 数组中出现次数超过一半的数字
思路:使用哈希映射(HashMap)来存储每个元素以及出现的次数。对于哈希映射中的每个键值对,键表示一个元素,值表示该元素出现的次数。用一个循环遍历数组 nums 并将数组中的每个元素加入哈希映射中。在这之后,遍历哈希映射中的所有键值对,返回值最大的键。同样也可以在遍历数组 nums 时候使用打擂台的方法,维护最大的值,这样省去了最后对哈希映射的遍历。
class Solution {
public Map<Integer,Integer> buildMap(int[] nums){
Map<Integer,Integer>res=new HashMap<Integer,Integer>();
for(int n:nums){
if(!res.containsKey(n)){
res.put(n,1);
}
else{
res.put(n,res.get(n)+1);
}
}
return res;
}
public int majorityElement(int[] nums) {
Map<Integer,Integer>dic=buildMap(nums);
Map.Entry<Integer,Integer>res=null;
for(Map.Entry<Integer,Integer>d:dic.entrySet()){
if(res==null||d.getValue()>res.getValue()){
res=d;
}
}
return res.getKey();
}
}