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/ ...
随机推荐
- django自带的cache缓存框架使用
https://docs.djangoproject.com/zh-hans/4.2/topics/cache/#top 主要步骤官网也写得很清楚了,包含怎么区使用. 这里就展示一些配置django- ...
- Windows手工入侵排查思路
文章来源公众号:Bypass Windows系统被入侵后,通常会导致系统资源占用过高.异常端口和进程.可疑的账号或文件等,给业务系统带来不稳定等诸多问题.一些病毒木马会随着计算机启动而启动并获取一定的 ...
- bash命令的使用
bash的工作特性之命令执行状态返回值和命令展开所涉及的内容及其示例演出 !脚本执行与调试 1.绝对路径执行,要求文件有执行权限 2.以sh命令执行,不要求文件有执行权限 3..加空格或source命 ...
- 通过JDK动态代理类实现一个类中多种方法的不同增强
1.为什么说JDK动态代理必须要实现当前父接口才能使用 JDK动态代理是基于接口的代理,它要求目标类(被代理的类)必须实现一个或多个接口.这是因为JDK动态代理是通过创建目标类的接口的代理对象来实现的 ...
- 获取yml自定义内容的方式
yml内容 yml: login: name: zhangsan age: 18 pass: 123456 方式一: 创建实体类 @Configuration @ConfigurationProper ...
- Draco使用笔记(1)——图形解压缩
目录 1. 概述 2. 详论 2.1. 工具 2.2. 代码 1. 概述 Draco是Google开发的图形压缩库,用于压缩和解压缩3D几何网格(geometric mesh)和点云(point cl ...
- JDK1.6在生产环境引起的坑
本文分享自华为云社区<[高并发]记一次JDK1.6在生产环境引起的坑!>,作者: 冰 河 . 最近有朋友遇到一个困惑:他写的程序在测试环境一点问题没有,但是发到生产环境却会频繁出现内存溢出 ...
- ModelArts黑科技揭秘|弹性训练,让训练资源张弛有度
摘要:AI进入产业的门槛变高,开发者想要做出优秀的AI模型就不得不在算力和成本之间折中,怎么办? 为帮助企业在AI落地过程中进一步实现降本增效,华为云推出AI黑科技--弹性训练. 今年,AI界最被热议 ...
- 基于ModelArts进行流感患者密接排查
摘要:针对疫情期间存在的排查实时性差.排查效率低.无法追踪密接者等问题,可以使用基于YOLOv4的行人检测.行人距离估计.多目标跟踪的方案进行解决. 本文分享自华为云社区<基于ModelArts ...
- 独家下载!突破开源Redis,华为云十年自研内核修炼之路《企业级Redis技术与应用解读》重磅发布
摘要:互联网业务神器最新揭秘:GaussDB(for Redis)如何以自研架构,突破开源版本限制,带来企业级稳定可靠?通过入门篇.性能篇.测评篇.应用篇四个章节,聚焦问题解决.场景应用和开发实战,分 ...