274. H 指数

一、题目描述

在这里插入图片描述

二、题目分析

这道题比较绕,理解题目意思,根据题目的说法,所谓的H指数就是:总共有 h 篇论文分别被引用了至少 h 次。且其余的 n - h 篇论文每篇被引用次数 不超过 h 次
解释示例2:
总共有1篇论文分别被引用了至少1次,且剩余的2篇论文每篇被引用次数不超过1次,之所以可以这么说,因为序号为2的这篇论文引用次数是3,大于1,所以被引用至少1次。而剩下的序号为1的论文和序号为3的论文引用次数均为1,不超过1次。所以H指数为1。当H指数为2的时候,显然不成立,所以1就是最大的H值。

三、解题思路

3.1 更换H指数定义

题目中给出的H指数定义比较绕,如果查询百度百科可以发现一个更加简单的H指数的定义:
在这里插入图片描述
那么问题就变得很简单了,将citations数组逆序排序,然后找到当前citations[i]<=i的下标i,返回i即可。比如示例1中的[6,5,3,1,0],发现i=3的时候满足条件,因此H指数为3。
解题的时候并没有采用逆序排序的方法,原因是Arrays工具类仅支持对基本类型数组升序排序,此时该如何做呢?

  1. 首先百度百科中的序号是从1开始的,而数组下标是从0开始的,百度百科中说的是直到某篇论文的序号大于该论文被引次数,序号减1就是H指数。换言之,也就是在降序数组中,直到某篇论文的索引(索引从0开始编号)大于等于该论文的被引次数,该索引就是H指数。
  2. 对百科中的条件进一步转换,百科中要求的是正向遍历降序数组,找到第一个索引值大于等于元素值的索引下标,换句话说,也就是找逆序遍历降序数组时,索引值小于元素值的索引下标+1的位置。
  3. 逆向遍历降序数组等同于正向遍历升序数组,并且升序数组中每一个元素的索引i对应着降序数组中的length - i - 1位置。
  4. 因此只需要正向遍历升序数组,找到第一个citations[i] > citations.length - 1的i,然后返回citations.length - i - 1 + 1
class Solution {
    public int hIndex(int[] citations) {
        Arrays.sort(citations);
        // 0 1 3 5 6
        // 6 5 3 3 0
        for (int i = 0; i < citations.length; i++) {
            if (citations[i] > citations.length - i - 1) {
                return citations.length - i;
            }
        }
        
        return 0;
    }
}

时间复杂度:O(nlogn),因为存在排序算法,空间复杂度O(1)

在这里插入图片描述

3.2 二分法枚举H指数

思路2采用的是一种常规思路,就是枚举求解H指数,但是枚举的过程使用二分枚举。降低枚举过程中的时间复杂度。
先说暴力枚举思路:

  1. 根据题意发现H指数最大就是数组的长度,因此H指数的范围是[0,length],比如[4,4,4,4]的H指数就是4,[0,0,0,0]的H指数就是0
  2. 第一层for循环枚举所有可能的H指数,然后对于每一个枚举的H指数,编写第二层for循环,判断当前H指数是否满足题目中的H指数判定条件,找到最大的H指数。
class Solution {
    public int hIndex(int[] citations) {
        return solve01(citations);
    }
    
    public int solve01(int[] citations) {
        int ans = 0;
        for (int i = 0; i <= citations.length; i++) {
            if (verifyH(citations, i)) {
                ans = Math.max(ans, i);
            }
        }
        return ans;
    }

    private boolean verifyH(int[] citations, int H) {
        int moreH = 0;
        for (int i = 0; i < citations.length; i++) {
            if (citations[i] >= H) {
                moreH++;
            } 
        }
        return moreH >= H;
    }
}

显然这种暴力枚举H的思路的时间复杂度是O(N),空间复杂度是O(1)

然后看二分枚举思路:

  1. left=0,right = citations.length,[left,right]是H可能的值
  2. H = (left + right) / 2
  3. 验证H是否合法,如果合法,说明可能存在更大的H,当然当前H也可能是最大的H,此时记录下H的值,将区间变为[H+1,right]
  4. 如果H不合法,说明不存在H篇论文至少被引用了H次,也就是少于H篇至少被引用了H次,此时缩小搜索区间[left, H-1]
class Solution {
    // 总体二分的思路就是二分枚举H指数
    // 对于枚举的H指数,找到最大的那一个
    // 假设当前考虑的H指数是N,如果刚好满足
    // 引用数组中有>N篇引用超过了N次,那么说明
    // 可能还有比当前N更大的H指数
    // 如果数组中>N篇引用低于N次,说明H指数枚举大了,需要缩小
    public int hIndex(int[] citations) {
        return solve02(citations);
    }

    public int solve02(int[] citations) {
        int left = 0;
        int right = citations.length;
        
        int ansH = 0;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            if (verifyH(citations, mid)) {
                ansH = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return ansH;
    }

    private boolean verifyH(int[] citations, int H) {
        int moreH = 0;
        for (int i = 0; i < citations.length; i++) {
            if (citations[i] >= H) {
                moreH++;
            } 
        }
        return moreH >= H;
    }
}

时间复杂度:O(nlogn),空间复杂度O(1)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值