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指数不用管了,没啥意义

### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值