哈希表

文章介绍了如何使用哈希表和双向链表实现LRU缓存机制,详细阐述了get和put操作的逻辑。此外,还列举了其他几个基于哈希映射的问题解决方案,如找只出现一次的数字、两数之和、数组数对计算及出现次数超过一半的数字的查找。
摘要由CSDN通过智能技术生成

一、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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值