274. H 指数
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 指数的更多相关文章
- Java实现 LeetCode 274 H指数
274. H指数 给定一位研究者论文被引用次数的数组(被引用次数是非负整数).编写一个方法,计算出研究者的 h 指数. h 指数的定义: "h 代表"高引用次数"(hig ...
- Leetcode 274.H指数
H指数 给定一位研究者论文被引用次数的数组(被引用次数是非负整数).编写一个方法,计算出研究者的 h 指数. h 指数的定义: "一位有 h 指数的学者,代表他(她)的 N 篇论文中至多有 ...
- 274 H-Index H指数
给定一位研究者的论文被引用次数的数组(被引用次数是非负整数).写一个方法计算出研究者的H指数.H-index定义: “一位科学家有指数 h 是指他(她)的 N 篇论文中至多有 h 篇论文,分别被引用了 ...
- [LeetCode] 274. H-Index H指数
Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...
- [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 ...
- [LeetCode] H-Index 求H指数
Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...
- [Swift]LeetCode274.H指数 | H-Index
Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...
- [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 ...
- H指数
H指数是用来综合衡量学者发表论文的数量和质量的指标,若某学者共发表N篇论文,H指数是指存在h 篇论文至少每篇有h 引用量,剩下的N-h篇中,每篇都不超过h引用量 计算H指数的方法:1.排序法思路:先将 ...
- 275 H-Index II H指数 II
这是 H指数 进阶问题:如果citations 是升序的会怎样?你可以优化你的算法吗? 详见:https://leetcode.com/problems/h-index-ii/description/ ...
随机推荐
- Python 潮流周刊第 33 期(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- CentOS 7 安装 Python 3.X版本
由于Centos7默认安装了python2.7.5版本,因此想安装python 3.X版本就需要特殊处理. 详情可以参考南宫羽香的技术博客原文:https://www.cnblogs.com/lclq ...
- MySQL|空间碎片化问题处理
一.空间碎片化严重案例分享 1.1 问题描述 实例磁盘空间近1个月上涨趋势明显,主要是个别日志表存储较大且部分表存在空间碎片化的现象. 1.2 处理流程 1.通过日常巡检以及监控发现某实例磁盘空间近1 ...
- MinIO FTP 断点续传
MinIO FTP 断点续传 对于minio来说,使用minio官方的Java SDK和开启FTP都是不支持断点续传的.对于要实现http接口的断点续传,可以通过调用Amazon S3 REST AP ...
- 文心一言 VS 讯飞星火 VS chatgpt (45)-- 算法导论6.1 3题
三.证明:在最大堆的任一子树中,该子树所包含的最大元素在该子树的根结点上. 文心一言: 在最大堆中,根结点是整个堆中最大元素的孩子,因此它包含的最大元素是在该子树的根结点上. 我们可以通过递归的方式证 ...
- Llama2-Chinese项目:6-模型评测
测试问题筛选自AtomBulb[1],共95个测试问题,包含:通用知识.语言理解.创作能力.逻辑推理.代码编程.工作技能.使用工具.人格特征八个大的类别. 1.测试中的Prompt 例如对于问 ...
- 中秋国庆花式玩法,用低代码DIY假日大屏“Vlog”
本文分享自华为云社区 <[云享热点]中秋国庆花式玩法,用低代码DIY假日大屏"Vlog"(内附节日福利)>,作者:华为云社区精选. 中秋.国庆双节将至,你的八天小长 ...
- 30亿参数,华为云发布全球最大预训练模型,开启工业化AI开发新模式
摘要: 4月25日,华为云发布盘古系列超大规模预训练模型,包括30亿参数的全球最大视觉(CV)预训练模型,以及与循环智能.鹏城实验室联合开发的千亿参数.40TB训练数据的全球最大中文语言(NLP)预训 ...
- 物联网设备上云难?华为云IoT帮你一键完成模型定义,快速在线调试设备
摘要:在不到3分钟的操作里,不仅完成了一款智慧烟感设备在云端的模型定义,还通过在线调试了解到了设备和远端通信的过程. 本文分享自华为云社区<物联网设备上云难?华为云IoT帮你一键完成模型定义,快 ...
- AI新手语音入门:认识词错率WER与字错率CER
摘要:本文介绍了词错率WER和字错率CER的概念,引入了编辑距离的概念与计算方法,从而推导得到词错率或字错率的计算方法. 本文分享自华为云社区<新手语音入门(一):认识词错率WER与字错率CER ...