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 > midleft =(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. cmd命令根据端口号杀进程

    1.根据端口查到进程pid netstat –ano|findstr 端口号 1 2.使用taskkill命令杀死进程 taskkill /pid pid 1 温馨提醒: 1.执行完第一步后,命令行显 ...

  2. Python笔记二之多线程

    本文首发于公众号:Hunter后端 原文链接:Python笔记二之多线程 这一篇笔记介绍一下在 Python 中使用多线程. 注意:以下的操作都是在 Python 3.8 版本中试验,不同版本可能有不 ...

  3. 安装Zabbix客户端zabbix-agent2

    Ubuntu系统:下载链接:http://mirrors.aliyun.com/zabbix/zabbix/6.0/ubuntu/pool/main/z/zabbix/?spm=a2c6h.25603 ...

  4. GeoServer发布地图服务(WMS、WFS)

    目录 1. 概述 2. 矢量数据源 3. 栅格数据源 1. 概述 我们知道将GIS数据大致分成矢量数据和栅格数据(地形和三维模型都是兼具矢量和栅格数据的特性).但是如果用来Web环境中,那么使用图片这 ...

  5. ChatGPT的中转站(欧派API) oupuapi,不扶墙也能上楼

    开启智能生活新篇章:oupo中转站(欧派)--引领人工智能大模型的枢纽 在人工智能技术日新月异的今天,我们荣幸地向您推介oupo中转站(欧派)--这一汇聚各类顶尖人工智能大模型的平台.它不仅为技术研发 ...

  6. Kubernetes常见错误总结

    1.屏幕持续打印Pod日志报error: unexpected EOF错误 Kubernetes: requesting flag for "kubectl logs" to av ...

  7. GDAL使用PROJ坐标转换相关问题的总结

    目录 1. 概述 2. 详论 2.1. 数据 2.2. PROJ库 2.3. 参考 1. 概述 GDAL是使用PROJ进行坐标转换的,但是很容易出现转换不了的问题,这里总结一二,以供参考. 2. 详论 ...

  8. 详解MRS CDL整体架构设计

    摘要:MRS CDL是FusionInsight MRS推出的一种数据实时同步服务,旨在将传统OLTP数据库中的事件信息捕捉并实时推送到大数据产品中去,本文档会详细为大家介绍CDL的整体架构以及关键技 ...

  9. 1g云主机升级centos8不满足centos 8 至少2g内存要求,linux虚拟内存来凑

    centos8 官方说,至少2g内存,推荐4g内存,像我的个人博客,zhoulujun.cn ,这种个人博客有不赚钱,丢个5美金一个月的1g内存,1核cpu,就够了. 强制升级到centos8,ngi ...

  10. 用 Java?试试简单的框架 Solon v1.10.9

    Java 国产的轻量级应用开发框架.可用来快速开发 Java 应用项目.主框架仅 0.1 MB.Helloworld: @Controller public class App { public st ...