LeetCode 981. 基于时间的键值存储 / 274. H 指数 / 275. H 指数 II

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 981. 基于时间的键值存储 / 274. H 指数 / 275. H 指数 II相关的知识,希望对你有一定的参考价值。

981. 基于时间的键值存储

2021.7.10 每日一题

题目描述

创建一个基于时间的键值存储类 TimeMap,它支持下面两个操作:

1. set(string key, string value, int timestamp)

存储键 key、值 value,以及给定的时间戳 timestamp。
2. get(string key, int timestamp)

返回先前调用 set(key, value, timestamp_prev) 所存储的值,其中 timestamp_prev <= timestamp。
如果有多个这样的值,则返回对应最大的  timestamp_prev 的那个值。
如果没有值,则返回空字符串("")。
 

示例 1:

输入:inputs = ["TimeMap","set","get","get","set","get","get"], inputs = [[],["foo","bar",1],["foo",1],["foo",3],["foo","bar2",4],["foo",4],["foo",5]]
输出:[null,null,"bar","bar",null,"bar2","bar2"]
解释:  
TimeMap kv;   
kv.set("foo", "bar", 1); // 存储键 "foo" 和值 "bar" 以及时间戳 timestamp = 1   
kv.get("foo", 1);  // 输出 "bar"   
kv.get("foo", 3); // 输出 "bar" 因为在时间戳 3 和时间戳 2 处没有对应 "foo" 的值,所以唯一的值位于时间戳 1 处(即 "bar")   
kv.set("foo", "bar2", 4);   
kv.get("foo", 4); // 输出 "bar2"   
kv.get("foo", 5); // 输出 "bar2"   

示例 2:

输入:inputs = ["TimeMap","set","set","get","get","get","get","get"], inputs = [[],["love","high",10],["love","low",20],["love",5],["love",10],["love",15],["love",20],["love",25]]
输出:[null,null,null,"","high","high","low","low"]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/time-based-key-value-store
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

还是前几天的哈希表,这次加了个二分查找,超过百分百

class TimeMap {
    //所有 TimeMap.set 操作中的时间戳 timestamps 都是严格递增的。
    //这句话有什么提示意义吗
    //哈希表存储键,值是一个node,node有两个属性,一个time,一个值value
    //node用一个list存储,因为set操作中时间戳是严格递增的
    //所以二分查找取相应的值就可以了
    class Node{
        int time;
        String value;
        public Node(int t, String v){
            time = t;
            value = v;
        }
    }
    Map<String, List<Node>> map;
    /** Initialize your data structure here. */
    public TimeMap() {
        map = new HashMap<>();
    }
    
    public void set(String key, String value, int timestamp) {
        List<Node> list = map.getOrDefault(key, new ArrayList<Node>());
        list.add(new Node(timestamp, value));
        map.put(key, list);
    }
    
    public String get(String key, int timestamp) {
        if(!map.containsKey(key))
            return "";
        else{
            List<Node> list = map.get(key);
            int size = list.size();
            int left = 0;
            int right = size - 1;
            while(left < right){
                int mid = (right - left + 1) / 2 + left;
                Node node = list.get(mid);
                if(node.time <= timestamp){
                    left = mid;
                }else{
                    right = mid - 1;
                }
            }
            if(list.get(left).time > timestamp)
                return "";
            return list.get(left).value;
        }
    }
}

/**
 * Your TimeMap object will be instantiated and called as such:
 * TimeMap obj = new TimeMap();
 * obj.set(key,value,timestamp);
 * String param_2 = obj.get(key,timestamp);
 */

换种二分查找的形式

class TimeMap {
    //所有 TimeMap.set 操作中的时间戳 timestamps 都是严格递增的。
    //这句话有什么提示意义吗
    //哈希表存储键,值是一个node,node有两个属性,一个time,一个值value
    //node用一个list存储,因为set操作中时间戳是严格递增的
    //所以二分查找取相应的值就可以了
    class Node{
        int time;
        String value;
        public Node(int t, String v){
            time = t;
            value = v;
        }
    }
    Map<String, List<Node>> map;
    /** Initialize your data structure here. */
    public TimeMap() {
        map = new HashMap<>();
    }
    
    public void set(String key, String value, int timestamp) {
        List<Node> list = map.getOrDefault(key, new ArrayList<Node>());
        list.add(new Node(timestamp, value));
        map.put(key, list);
    }
    
    public String get(String key, int timestamp) {
        if(!map.containsKey(key))
            return "";
        else{
            List<Node> list = map.get(key);
            int size = list.size();
            int left = 0;
            int right = size;
            while(left < right){
                int mid = (right - left) / 2 + left;
                Node node = list.get(mid);
                if(node.time <= timestamp){
                    left = mid + 1;
                }else{
                    right = mid;
                }
            }
            if(left - 1 < 0)
                return "";
            return list.get(left - 1).value;
        }
    }
}

/**
 * Your TimeMap object will be instantiated and called as such:
 * TimeMap obj = new TimeMap();
 * obj.set(key,value,timestamp);
 * String param_2 = obj.get(key,timestamp);
 */

274. H 指数

2021.7.11 每日一题

题目描述

给定一位研究者论文被引用次数的数组(被引用次数是非负整数)。编写一个方法,计算出研究者的 h 指数。

h 指数的定义:h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。且其余的 N - h 篇论文每篇被引用次数 不超过 h 次。

例如:某人的 h 指数是 20,这表示他已发表的论文中,每篇被引用了至少 20 次的论文总共有 20 篇。

 

示例:

输入:citations = [3,0,6,1,5]
输出:3 
解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。
     由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/h-index
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这题出的,总共有h篇论文引用至少h次,那么如果是[1,3,1],应该输出多少了
给的答案是1,那就意思是有1篇论文至少引用1次才对,其实意思是3篇论文至少引用了一次,引用次数大于等于1的论文数量要大于引用次数1,所以输出1。。所以输出的应该是次数,题目应该写成,至少h篇论文引用了h次。。。
算了,没啥可纠结的,计数完事了,其实很简单的一道题。题解里很多二分查找的,如果不让开新的空间可以用二分
看下一道题吧,这里理解的有点偏差

class Solution {
    public int hIndex(int[] citations) {
        int max = 0;
        int l = citations.length;
        for(int i = 0; i < l; i++){
            max = Math.max(max, citations[i]);
        }
        int[] count = new int[max + 1];
        for(int i = 0; i < l; i++){
            count[citations[i]]++;
        }
        
        int res = 0;
        //引用次数
        for(int i = max; i >= 0; i--){
            //检查引用次数等于i的文章数
            res += count[i];
            //如果有res篇文章引用数超过i
            if(res >= i)
                return i;
        }
        return 0;
    }
}

275. H 指数 II

题目描述

给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照 升序排列 。
编写一个方法,计算出研究者的 h 指数。

h 指数的定义: “h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数不多于 h 次。)"

 

示例:

输入: citations = [0,1,3,5,6]
输出: 3 
解释: 给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 0, 1, 3, 5, 6 次。
     由于研究者有 3 篇论文每篇至少被引用了 3 次,其余两篇论文每篇被引用不多于 3 次,所以她的 h 指数是 3。
 

说明:

如果 h 有多有种可能的值 ,h 指数是其中最大的那个。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/h-index-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

h是论文篇数…
这个题咋回事呢,一直想用left < right 写,结果搞了半天也搞不对,好烦,最后只能按官解改了个这样的二分,想想为什么
首先明确题意,到现在我还是不知道要输出的是文章数还是被引次数,例如[1,1,3],答案是1,那么按题中所给的意思就是1篇论文被引次数至少1次,其余2篇不多余1次。
[100]输出是1,意思是1篇论文引用次数至少1次。
加上括号里解释的那句话我现在才好像读懂题的意思了,意思就是说这两个部分可以用重叠,那么[1,1,3]就可以说通了

class Solution {
    public int hIndex(int[] citations) {
        //二分查找
        int l = citations.length;
        int left = 0;
        int right = l - 1;
        while(left <= right){
            //mid没有具体含义,就是个下标
            int mid = (right - left) / 2 + left;
            //当前被引次数
            int count = citations[mid];
            //如果文章数大于引用次数,说明符合条件,试着增大被引次数
            if(count < l - mid){
                left = mid + 1;
            //如果文章数小于引用次数,那么减少被引次数
            }else{
                right = mid - 1;
            }
        }
        return l - left;
    }
}

看了weiwei哥的二分查找,终于写出了一个符合我意思的二分。我习惯写这种二分就是跟着weiwei哥学的,不会了还得看weiwei哥啊,哈哈
这里首先要明确,mid不是指文章数,也不是指引用次数,它只是一个下标位置,可以理解为一个分割线,而我们要做的就是找到这个分割线最合适的位置。这个分割线得理解正确了才能写出二分的正确形式,再看题的意思,有h篇论文分别被引用至少h次。那么这个意思就是说论文篇数是小于等于引用次数的。分割线找的是,右边的最少引用次数大于等于右边的论文篇数
那么明白了一点,二分就好写了。先找分割线mid,如果分割线右边的最少引用次数也就是mid下标对应的值小于等于右边的论文篇数,说明是符合条件的,所以right = mid;反之,如果引用次数小于论文篇数,那么不符合条件,分割线靠左了,那么left = mid + 1
最后分割线是满足条件的最小位置,输出的是论文篇数,也就是l - left

自己写的时候理解不到位,反了

class Solution {
    public int hIndex(int[] citations) {
        //二分查找
        int l = citations.length;
        int left = 0;
        int right = l - 1;
        //特殊情况,如果最右边的位置是0,那么没有符合条件的,就输出0
        if(citations[right] == 0)
            return 0;
        while(left < right){
            //mid没有具体含义,就是个下标
            int mid = (right - left) / 2 + left;
            //当前被引次数
            int count = citations[mid];
            //如果文章数大于引用次数,说明分割线靠左了,试着增大分割线的位置
            if(count < l - mid){
                left = mid + 1;
            //如果文章数小于等于引用次数,那么分割线靠右边了,尝试往左
            }else{
                right = mid;
            }
        }

        return l - left;
    }
}

那么到了这里再回过头来想昨天那个题为什么res > i,输出i,也就是引用次数就可以,而不是说输出res论文篇数呢。因为有这样一句话“(其余的 N - h 篇论文每篇被引用次数不多于 h 次)”,所以 i 是要小于等于res的,直接输出res的话,论文篇数可能包含了括号里面的论文,所以不准确。而输出 i 的话,是满足条件的最大的论文篇数
有点绕,建议直接理解这个二分,第一道H指数不用管了,没啥意义

以上是关于LeetCode 981. 基于时间的键值存储 / 274. H 指数 / 275. H 指数 II的主要内容,如果未能解决你的问题,请参考以下文章

文巾解题 981. 基于时间的键值存储

红黑树981. 基于时间的键值存储(中等)

[MSTL] lc981. 基于时间的键值存储(设计+哈希表+map二分查找+代码技巧)

为高级查询选择基于 Java 的键值存储

可扩展对象的键值存储

时间序列数据的键值存储?