1.题目介绍

给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。

根据维基百科上 h 指数的定义:h 代表“高引用次数” ,一名科研人员的 h 指数 是指他(她)至少发表了 h 篇论文,并且每篇论文 至少 被引用 h 次。如果 h 有多种可能的值,h 指数 是其中最大的那个。

示例 1:

输入:citations = [3,0,6,1,5]

输出:3

解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。

由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。

示例 2:

输入:citations = [1,3,1]

输出:1

提示:

n == citations.length

1 <= n <= 5000

0 <= citations[i] <= 1000

2.题解

我们就将这个问题转化一下:找到满足 共有h个citations[i] > h的 最大h值

这里有一个关键问题,这里有两个变量h和i,能否找到这两个变量之间的关系是解决问题的关键

2.1 贪心算法(排序)

思路

这里我们正常的思路是不是设置 h = 0,然后每找到一个citations[i] > h的数就令h++?

但是这里有一个问题,我可能在h=0的时候,citations[n] >= 0,但是当h++之后呢?就不一定了,之前能满足的条件现在不能满足,这里就是因为同时存在两个变量i和h导致的连续性问题。

那我们先将数组按从大到小的顺序进行排序呢?

根据传递性,若有citations[n] >= h,那么citations[0]>=citations[1]>=...>=citations[n] >= h,所以这里是没有问题的。

代码

1.使用新变量h

这里注意第一个数比较的对象应该是h=1,所以使用++h;最后退出的h是不满足条件的第一个h,而满足的最大h为h-1.

class Solution {
public:
int hIndex(vector<int>& citations) {
std::sort(citations.begin(), citations.end(), [](int a, int b) {
return a > b;
});
int h = 0;
for (auto num:citations)
if (num < ++h) break;
return h-1;
}
};

2.使用下标代替h

我们在实际上观察发现,有一个变量和h一样也是不断自加的,而且比h更好用(不用),那就是数组的下标。

但是这里由于这里下标h从0开始,然后我们第一个数比较的应该是h+1 = 1,所以这里我们使用 >h,而不是 >=h

class Solution {
public:
int hIndex(vector<int>& citations) {
std::sort(citations.begin(), citations.end(), [](int a, int b) {
return a > b;
});
int h = 0;
while(h < citations.size() && citations[h] > h){
h++;
}
return h;
}
};

复杂度分析

时间复杂度:\(O(n \log n)\),其中 n 为数组 \(\textit{citations}\) 的长度。即为排序的时间复杂度。

空间复杂度:\(O(\log n)\),其中 n 为数组 \(\textit{citations}\) 的长度。即为排序的空间复杂度。

2.2 计数排序

思路

根据上述解法我们发现,最终的时间复杂度与排序算法的时间复杂度有关,所以我们可以使用计数排序算法,新建并维护一个数组 counter 用来记录当前引用次数的论文有几篇。

根据定义,我们可以发现 H 指数不可能大于总的论文发表数,所以对于引用次数超过论文发表数的情况,我们可以将其按照总的论文发表数来计算即可。这样我们可以限制参与排序的数的大小为 [0,n](其中 n 为总的论文发表数),使得计数排序的时间复杂度降低到 O(n)。

最后我们可以从后向前遍历数组 counter,对于每个 0≤i≤n,在数组 counter 中得到大于或等于当前引用次数 i 的总论文数。当我们找到一个 H 指数时跳出循环,并返回结果。

这里count数组记录的是引用次数为i的论文有几篇,这里的i既表示引用次数为i的有count[i]篇,且目前找到的是引用次数为i次的论文;题目至少发表的论文数目前为res,要求每篇论文至少被引用res次,若第一次出现 res >= i 也就是现在找到的论文引用次数小于要求每篇论文至少的引用次数,已经不满足要求,故退出循环并返回i。

这里为什么是res >= i呢?因为有这样的一种可能,我现在正处于边界值(res == i-1),再来一篇这样的论文,就有res == i了,但目前引用次数为i的论文有两篇及以上,res += count[i];的话 就有res > i了,但是如果我只取其中任意一篇出来,是的 res == i,还是满足至少发表了 i 篇论文,并且每篇论文至少被引用 i 次的条件的。之后无论我只取count[i]中的几篇都是res(新)>res(旧)>i(旧)>i--(新)就肯定是不满足的情况,

代码

class Solution {
public:
int hIndex(vector<int>& citations) {
int n = citations.size();
vector<int> count(n+1);
for (int num:citations) count[min(n,num)]++;
int res = 0;
for (int i = n; i >= 0; i--){
res += count[i];
if (res >= i) return i;
}
return -1; //never
}
};

2.3 二分搜索

二分搜索的思路是把我们要找的满足至少发表了 h 篇论文,并且每篇论文至少被引用 h 次的h值作为二分搜索的对象,比如像正常我们的思路是像上方那样从n(总论文数)开始一点点向下寻找h值,这里我们就直接从\(\frac{n}{2}\)开始寻找,满足就在右区间进行寻找,不满足就在左区间进行寻找,直到左右指针重合或错开。

代码

1. while (left < right){} / while (left != right){}(循环结束条件)

  • 这里因为有right = mid - 1;的存在,当mid + 1 == left
  • right = mid -1 == left,当两指针重合之时,也就是找到了真正的h值。

2. mid = (left+right+1)>>1;为何要加1?(死循环的避免)

为防止循环卡死,若使用 mid = (left+right)>>1,考虑如下情况:

  • 当论文数组为{0}时,只有一篇引用次数为0的论文
  • 这时候mid = (0+1)>>1 = 0
  • (citations[0] = 0) >= (mid = 0), cnt++ = 1
  • (cnt = 1) > (mid = 0), 故 left = mid =0
  • left = 0,right = 1, 陷入死循环

总而言之,当递进到了 left + 1 == right 这种情况时,

left + right 肯定为一奇数,向下取整,即mid =[(left+right)/2 ]向下取整= (left+right-1)/2

当cnt > mid , 且mid = left 则会陷入死循环

而(left+right-1)/2 =(left+left+1 -1)/2 = left, 正是陷入死循环的条件

如要避免我们只要使mid向上取整即可,mid =[(left+right)/2 ] 向上取整 = (left+right+1)/2

这样即使有cnt > mid,left =(left+right+1)/2 =(left+left+1 +1)/2 = left + 1,正好left=right,圆满结束!

3.left = mid; / right = mid - 1;(二分查找核心)

  • 由这里的判断条件cnt>=mid
  • 如果成立,说明h的取值在[mid,right]之间,所以left = mid;
  • 如果不成立,说明h的取值在[left, mid)之间,所以right = mid - 1;
class Solution {
public:
int hIndex(std::vector<int>& citations) {
int left = 0, right = citations.size();
int mid = 0, cnt = 0;
while (left != right){
mid = (left+right)>>1; //将区域中间值作为至少引用论文值,这里+1确保mid总是向上取整,防止出现死循环
cnt = 0;
for (int i = 0; i < citations.size(); i++){
// 寻找大于等于mid的论文数量cnt
if (citations[i] >= mid){
cnt++;
}
}
// 如果找到的论文数量大于至少引用数,说明可行,mid实际值在右区间
if (cnt >= mid) left = mid;
else right = mid - 1;
}
return left;
}
};

复杂度分析

  • 时间复杂度:O(nlogn)O(n log n)O(nlogn),其中 nnn 为数组 citations\textit{citations}citations 的长度。需要进行 lognlog nlogn 次二分搜索,每次二分搜索需要遍历数组 citations\textit{citations}citations 一次。
  • 空间复杂度:O(1)O(1)O(1),只需要常数个变量来进行二分搜索。

274. H 指数的更多相关文章

  1. Java实现 LeetCode 274 H指数

    274. H指数 给定一位研究者论文被引用次数的数组(被引用次数是非负整数).编写一个方法,计算出研究者的 h 指数. h 指数的定义: "h 代表"高引用次数"(hig ...

  2. Leetcode 274.H指数

    H指数 给定一位研究者论文被引用次数的数组(被引用次数是非负整数).编写一个方法,计算出研究者的 h 指数. h 指数的定义: "一位有 h 指数的学者,代表他(她)的 N 篇论文中至多有 ...

  3. 274 H-Index H指数

    给定一位研究者的论文被引用次数的数组(被引用次数是非负整数).写一个方法计算出研究者的H指数.H-index定义: “一位科学家有指数 h 是指他(她)的 N 篇论文中至多有 h 篇论文,分别被引用了 ...

  4. [LeetCode] 274. H-Index H指数

    Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...

  5. [LeetCode] 275. H-Index II H指数 II

    Follow up for H-Index: What if the citations array is sorted in ascending order? Could you optimize ...

  6. [LeetCode] H-Index 求H指数

    Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...

  7. [Swift]LeetCode274.H指数 | H-Index

    Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...

  8. [Swift]LeetCode275. H指数 II | H-Index II

    Given an array of citations sorted in ascending order (each citation is a non-negative integer) of a ...

  9. H指数

    H指数是用来综合衡量学者发表论文的数量和质量的指标,若某学者共发表N篇论文,H指数是指存在h 篇论文至少每篇有h 引用量,剩下的N-h篇中,每篇都不超过h引用量 计算H指数的方法:1.排序法思路:先将 ...

  10. 275 H-Index II H指数 II

    这是 H指数 进阶问题:如果citations 是升序的会怎样?你可以优化你的算法吗? 详见:https://leetcode.com/problems/h-index-ii/description/ ...

随机推荐

  1. 数字孪生与VR设备的融合为旅游行业带来革新

    数字时代的推动下,旅游行业正迎来一场革命性的变革.数字孪生系统与虚拟现实(VR)的融合为旅游体验带来了全新的可能性.通过数字孪生技术的实时模拟和VR设备的沉浸式体验,旅游行业迎来了全新的变革时代. 数 ...

  2. 数字孪生系统融合GIS系统能够在洪涝灾害防治上带来什么帮助?

    数字孪生技术与GIS系统的融合,为防治洪涝灾害方式带来了巨大的改变.这种整合的力量超越了过去单一技术的局限,为防洪抗灾工作提供了更全面.更准确的决策支持和应急响应能力. 在过去,防洪抗灾工作主要依赖于 ...

  3. 【C#】【System.Linq】一些便捷的数据处理方法(Range、Select)

    因为用习惯了Python中一些便捷的方法,随即查询C#中有没有类似的. 一.Range()方法 在Python中,range(Start,End,Step)可以直接生成一个可迭代对象,便用于需要循环多 ...

  4. 结合 element -Plus组件库,压缩图片大小,限制图片格式

    业务背景:业务上需求满足上传的图片不能太大,但是有时候上传的图片确实超过了限制大小,所以前端这边可以将图片压缩再上传,亦或者是上传给后端接口的图片只能是指定格式,我们前端需要将图片后缀转化,也可以处理 ...

  5. Python——第五章:处理异常try、except、else、finally

    处理异常try 和 except 在 Python 中,try 和 except 语句用于处理异常(错误).通过使用这两个关键字,你可以编写代码来捕获和处理可能发生的异常,以保持程序的稳定性. try ...

  6. elasticsearch oom问题分析

    背景 线上发现elasticsearch集群状态red,并且有个es节点jvm内存使用不断升高,直到gc后依然内存不够使用,服务停止.查看日志,elasticsearch出现OOM报错. [2023- ...

  7. [极客大挑战 2019]EasySQL 1

    [极客大挑战 2019]EasySQL 1 观察题目,发现为登录界面,判断这道题的考点是SQL注入. 知识点 万能密码 知识点原理 当用户尝试登录时 网站后台会进行SQL查询,比如 [select * ...

  8. 从Bitcask存储模型谈超轻量级KV系统设计与实现

    Bitcask介绍 Bitcask是一种"基于日志结构的哈希表"(A Log-Structured Hash Table for Fast Key/Value Data) Bitc ...

  9. 常见的6种MySQL约束

    摘要:一篇文章带你彻底了解MySQL各种约束 MySQL约束 <1> 概念 是一种限制,它是对表的行和列的数据做出约束,确保表中数据的完整性和唯一性. <2> 使用场景 创建表 ...

  10. PPT 放映时字体缺失怎么办

    PPT 放映时字体缺失怎么办 文字转成图片 嵌入字体 没有版权的不给嵌入 安装字体 http://www.hellofont.cn 在线使用安装字体