[排序 二分查找] 274. H 指数(比较排序 → 计数排序)275. H 指数 II(线性查找 → 二分查找)

274. H 指数(citations数组无序)

题目链接:https://leetcode-cn.com/problems/h-index/


分类:

  • 数学:新概念“h指数” ,即寻找最小的 i 使len-i <= citations[i]成立,len - i 就是h指数;
  • 排序:
    • 比较排序:Arrays.sort对数组排序,在有序数组中寻找第一个满足len-i <= citations[i]的 i;
    • 计数排序:辅助数组count统计citations中“不同被引用次数”对应的论文数量(即count[i]存放被引用次数 = i 的论文个数),从后往前寻找第一个满足 i <= sum(count[n … i])的 i 。

在这里插入图片描述
.
.

思路1:排序 (O(NlogN))

首先,将数组排序,例如citations = [3,0,6,1,5],排序后得到:[0,1,3,5,6];

然后,遍历排序数组寻找最大的h指数,即寻找满足len - i <= citations[i] 的最大len - i,也就是寻找最小的 i 使len - i <= nums[i]成立,此时len - i就是最大的h指数。

class Solution {
    public int hIndex(int[] citations) {
        Arrays.sort(citations);
        int len = citations.length;
        //寻找排序数组里满足len-i>=nums[i]的最大i
        int res = 0;
        for(int i = 0; i < len; i++){
            if(citations[i] >= len - i){
                return len - i;
            }
        }
        return res;
    }
}
  • 时间复杂度:O(NlogN)

思路2:计数排序(O(N))

开辟一个数组count,count[i]存放的是被引用次数 == i的论文个数。但citations数组中元素值大小没有固定的上界,也就是论文引用次数没有固定上界,那么count数组的大小就不好设置了。

这里我们可以利用一个性质:h指数最大不可能超过 citations数组的大小

例如:[100,100,100,100,100],h=5,

所以我们在对citations做计数排序时,如果遇到元素值 > citations.length的元素,就取作citations.length,而限制最大引用次数并不会影响到h的结果。所以count数组的大小设置为[len+1]即可。

接下来对citations数组做计数排序,统计各个引用次数下的论文个数,填充到count数组:

例如:citations=[1,3,2,3,100]
citations[0]=1,则count[1]++;
citations[1]=3,则count[3]++;
citations[2]=2,则count[2]++;
citations[3]=3,则count[3]++;
citations[4]=5(100>5,取5),则count[5]++;
最终得到:count=[0,1,1,2,0,1]

接着在count数组上统计被引用至少 i 次的论文个数,用sumCount数组来存放:

  • 被引用至少 i 次的论文个数 = count[i]+count[i+1]+…+count[n]。
例如:count=[0,1,1,2,0,1]
i=0表示论文至少被引用0次,则count[i=0~n]之和=5,即有5篇论文至少被引用0次,所以sumCount[0]=5;
i=1表示论文至少被引用1次,则count[i=1~n]之和=5,即有5篇论文至少被引用1次,所以sumCount[1]=5;
i=2表示论文至少被引用2次,则count[i=2~n]之和=4,即有4篇论文至少被引用2次,所以sumCount[2]=4;
...以此类推,可以得到:
sumCount=[5,5,4,3,1,1],sumCount[i]表示至少有 i 次引用的论文数量。

最后,我们在sumCount中寻找最大的满足 i <= sumCount[i] 的 i 就是h指数,所以这里的h指数=3.

实现代码:

class Solution {
    public int hIndex(int[] citations) {
        int n = citations.length;
        int[] count = new int[n + 1];//count[i]存放的是被引用次数==i的论文个数

        //citations做计数排序
        for(int elem : citations){
            count[Math.min(n, elem)]++;
        }
        int[] sumCount = new int[n + 1];//sumCount[i]表示至少有 i 次引用的论文数量
        sumCount[n] = count[n];
        for(int i = n - 1; i >= 0; i--){
            sumCount[i] = sumCount[i + 1] + count[i];
        }
        //寻找sumCount中的h指数
        for(int i = n; i >= 0; i--){
            if(i <= sumCount[i]) return i;
        }
        return 0;
    }
}
  • 时间复杂度:O(N),计数排序需要遍历一次数组O(N),构造sumCount需要O(N),寻找h指数需要O(N),实际时间复杂度为O(3N)。
  • 空间复杂度:O(N),使用到了count,sumCount两个辅助数组,实际空间复杂度为O(2N).

优化:

  • 空间优化:第二个辅助数组sumCount可以省略,我们可以对count数组从后往前遍历,设置一个sum变量在遍历过程迭更新来替代 i <= sumCount[i] 中的sumCount[i]。(和常用的动态规划空间优化手段类似)

  • 时间优化:在sum变量迭代更新的同时可以寻找最大的满足i <= sum的i,也就是从后往前找到的第一个 <= sum的i,可以减少一次遍历数组的过程。

	//思路2的优化版本
	class Solution {
	    public int hIndex(int[] citations) {
	        int n = citations.length;
	        int[] count = new int[n + 1];//count[i]存放的是被引用次数==i的论文个数
	
	        //citations做计数排序
	        for(int elem : citations){
	            count[Math.min(n, elem)]++;
	        }
	        int sum = count[n];//sumCount优化为一个迭代变量
	        //从后往前寻找h指数
	        for(int i = n; i >= 0; i--){
	            if(i <= sum) return i;
	            sum += count[i - 1];
	        }
	        
	        return 0;
	    }
	}

275. H 指数 II (citations数组有序)

题目链接:https://leetcode-cn.com/problems/h-index-ii/


分类:

  • 数学:新概念“h指数” ,即寻找最小的 i 使len-i <= citations[i]成立,len - i 就是h指数;
  • 查找:
    • 线性查找:同274思路1,遍历数组寻找找第一个满足len-i <= citations[i]的 i (O(N));
    • 二分查找:二分查找满足citations[i] >= len - i的最小 i (O(logN))。

在这里插入图片描述

题目分析

275和274相比,给定的citations数组是有序的,所以一个思路就是直接利用274的思路1实现O(N)用时的解法(思路1);

本题的考点在于,将查找时间优化为O(logN),就是在有序数组上做二分查找,寻找满足citations[i] >= len - i 的最小 i 。

思路1:遍历查找 O(N)

和274思路1相同,但省去了排序步骤,所以时间复杂度相比274思路1降为O(N)。

寻找h指数,就是在升序的citations数组中寻找最小的i使citations[i] >= len - i,此时的len-i就是h指数。

class Solution {
    public int hIndex(int[] citations) {
        for(int i = 0; i < citations.length; i++){
            if(citations[i] >= citations.length - i) return citations.length - i;
        }
        return 0;
    }
}

思路2:二分查找 O(logN)

这题要使用二分查找,关键就在于寻找二分查找所依据的不等式,然后根据不等式设计二分查找规则和边界情况处理:

首先,明确查找目标:满足citations[i] >= len - i的最小 i,即如果存在相等的情况,就取相等时的下标 i;如果不存在相等的情况,就取满足citations[i] > len - i 的最小 i。

然后,设计二分查找流程,和常规的二分查找基本相同,只是在退出循环时left,right的处理上有所不同:

取中位点mid=(left+right)/2:

  • 如果citations[mid] == len - mid ,返回 len - mid;
  • 如果citations[mid] < len - mid,说明 >= len - mid的元素只可能在右半区间,令left=mid+1;
  • 如果citations[mid] > len - mid,说明满足 >= len - mid的更小的 i 只可能在左半区间,令right=mid-1;

当left>right时退出循环,说明数组中不存在citations[i] == len - i 的情况,所以此时的left就是满足citations[i]>len-i的最小下标 i,返回len - left就是h指数。

例如:

[0,1,4,5,6] len=5 , h = 3
left=0,right=4, mid=2 nums[mid]=4> 5-2=3,right=1
left=0,right=1, mid=0 nums[mid]=0< 5-0, left=1
left=1,right=1, mid=1,nums[mid]=1< 5-1=4,left=2
left=2>right=1,退出循环。
h = len - left = 5 - 2 = 3.

实现代码:

class Solution {
    public int hIndex(int[] citations) {
        int len = citations.length;
        int left = 0, right = len - 1;
        while(left <= right){
            int mid = (left + right) / 2;
            if(citations[mid] == len - mid) return len - mid;
            else if(citations[mid] < len - mid) left = mid + 1;
            else if(citations[mid] > len - mid) right = mid - 1;
        }
        //执行到这里,说明数组中不存在citations[i] == len - i,
        //只能寻找满足citations[i]>len-i最小的i,就是退出循环时的left
        return len - left;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值