275.H指数II
1.题目介绍
给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数,citations 已经按照 升序排列 。计算并返回该研究者的 h 指数。
h 指数的定义:h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (n 篇论文中)至少 有 h 篇论文分别被引用了至少 h 次。
请你设计并实现对数时间复杂度的算法解决此问题。
2.题解
2.1 模拟
思路分析
这里使用
代码
class Solution {
public:
int hIndex(vector<int>& citations) {
int h = 0;
for (int i = citations.size() - 1; i>=0; i--){
if (citations[i] > h) h++; //这里要考虑到我加入一篇新论文之后,比较的论文总数是h+1而不是h,
else return h;
}
return h; // 若是所有的论文引用次数,都大于n便移动到此处
}
};
复杂度分析
- 时间复杂度:O(n) 这里并不满足题目要求!所以我们考虑二分查找思路
2.2 二分查找
思路分析
这里数组已经按升序排列,题目要求要用对数时间复杂度,所以我们就可以直接针对数组进行二分查找。
但是注意这里比较容易错的是,这里是升序排列,所以我们计算已经加入的论文数目时,并不是使用 mid(当前下标) + 1 来计算, 而是使用 len - mid 来进行计算。
原理:比如像结尾:return len - left;
我们使用len - left = 尾下标 + 1 - left;
根据种树原理,尾指针-起始指针+1 = 个数
。
也就是说:尾下标 - left + 1
就是我们需要的论文个数
同理 if (citations[mid] >= len - mid)
中使用 len - mid计算已经加入的论文个数
(这里的mid是下标,len是总数目,二者相减计算的刚好是相差的数目)
代码
这里len - mid 表示的就是现在所取的论文数目,mid为其所处数组的下标位置
这里的left,right都是按下标来算的,
这里其实上是一个左开右闭区间(left左边节点不成立,right(包括right)右边节点成立)
对于while (left < right)
当left = right - 1 时,
由于int mid = left + (right - left) / 2;,
向下取整 mid = left;
有两种情况:
1.当前mid节点满足条件,right = mid(left) = right - 1; right向左推进一个,与left(mid)重合,此时left左边节点均不成立,right(包含right)右边节点均成立,所以这里所求下标值即为left/right,这里论文的个数即为 len - left/right。
2.当前mid节点不满足条件,left = mid(left) + 1 = right; left向右推进一个,与right(mid+1)重合,所以这里所求下标值即为left/right,这里论文的个数即为 len - left/right。
class Solution {
public:
int hIndex(std::vector<int>& citations) {
int len = citations.size();
int left = 0, right = len;
while (left < right){
int mid = left + (right - left) / 2;
//确保在计算 mid 时不会发生溢出,实际计算和(right+left)/2等效
//这里注意mid的值越大成立越容易
if (citations[mid] >= len - mid) {
right = mid;//mid成立了。根据连续性,右边更大的节点肯定也成立, h <= mid,在(...,mid]中探寻h的值
} else {
left = mid + 1; //mid肯定是不成立了。根据连续性,左边更小的节点也不成立,h > mid,在[mid+1, ...]即(mid,...]中探寻h的值。
}
}
return len - left;
}
};
2.3 通过开闭区间来进行理解
思路分析
1.这里将mid当作当前加入论文的最大数目
if (citations[n - mid] >= mid) ,这里使用 n - mid = 尾指针 + 1 - 数目(包括起始和尾指针) = 尾指针 - [ 数目(包括起始和尾指针)- 1] = 尾指针 - 移动数目 = 起始指针。
总而言之,这里的mid因为包含的是起始->尾部 所有的数目 = 移动数目 + 1; 而 n 恰好为尾指针 +1 两个1抵消。
2. 对于left 和 right 的讨论
对于所处闭区间,使用 left/right = mid;
对于所处开区间,使用 left/right = mid + 1 / mid - 1;
首先是闭区间,结束条件也就是区间不成立的情况:
3.结束条件的讨论:
注意:二分的区间不一定包含答案,答案可能在二分的区间外。
3.1闭区间【left左边的点均成立,right右边的点均不成立】
while (left <= right)(这里的left/right指向的是h指数所在区间,不是指向的引用次数数组)
当到达 left == right 的时候,有两种情况:
1.该mid是成立,left = mid(即left) + 1;
由于right右边所有的点均已证明为不成立,left+1正好指向的是此时right右边的第一个不成立点;
而right指向的点(为left加一之前指向的点)左边的点已证明均成立,
所以实际上要返回的是right所对应的。
2.该mid是不成立,right = mid(即right/left) - 1;
由于left是由成立的mid+1而来,所以left前面所有可能得论文次数都是成立的,
right回退到最大的那个点上,返回right
补充:这里left = h + 1; right = h; 故return left - 1 / return right均可。
且注意这里由于都是闭区间,所以可从[1,n]作为初始区间,至于为何是从1而不是0开始,请看下方解释。
假设最后的mid = 3,下图分别对应两种情况最终位置:
// 闭区间
class Solution {
public:
int hIndex(vector<int> &citations) {
// 在区间 [left, right] 内询问
int n = citations.size();
int left = 1;
int right = n;
while (left <= right) { // 区间不为空
// 循环不变量:
// left-1 的回答一定为「是」
// right+1 的回答一定为「否」
int mid = (left + right) / 2; // left+(right-left)/2
// 引用次数最多的 mid 篇论文,引用次数均 >= mid
if (citations[n - mid] >= mid) {
left = mid + 1; // 询问范围缩小到 [mid+1, right]
} else {
right = mid - 1; // 询问范围缩小到 [left, mid-1]
}
}
// 循环结束后 right 等于 left-1,回答一定为「是」
// 根据循环不变量,right 现在是最大的回答为「是」的数
return right;
}
};
作者:灵茶山艾府
链接:https://leetcode.cn/problems/h-index-ii/solutions/2504326/tu-jie-yi-tu-zhang-wo-er-fen-da-an-si-ch-d15k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.2左闭右开区间【left左边的点均成立,right(包括right)右边的点均不成立】
while (left < right)(比如像[4,5),由于数目不是小数,故此时已经可以结束循环)
当到达left = right - 1时
由于这里是向下取整, mid == left;
这里也有两种情况
1.该mid是成立,left = mid(left) + 1 = right;
而又因为左闭区间,left左侧所有点均成立均是成立的
由于这里右边是开区间,表明包括从right开始(包括right)的点是不成立的
故h = (left - 1)或 (right - 1);
2.该mid是不成立,right = mid(right - 1);
此时 left == right;
由于这里左闭区间,所以left左边所有的点均成立。
由于这里右开区间,所以right包括right右边所有的点均不成立。
故h = (left - 1)或 (right - 1);
补充:且注意这里由于左闭右开,所以可从[1,n+1) (选用n+1就保证了n在区间内)作为初始区间,至于为何是从1而不是0开始,请看下方解释。
假设最后的mid = 3,下图分别对应两种情况最终位置:
// 左闭右开区间
class Solution {
public:
int hIndex(vector<int> &citations) {
// 在区间 [left, right) 内询问
int n = citations.size();
int left = 1;
int right = n + 1;
while (left < right) { // 区间不为空
// 循环不变量:
// left-1 的回答一定为「是」
// right 的回答一定为「否」
int mid = (left + right) / 2;
// 引用次数最多的 mid 篇论文,引用次数均 >= mid
if (citations[n - mid] >= mid) {
left = mid + 1; // 询问范围缩小到 [mid+1, right)
} else {
right = mid; // 询问范围缩小到 [left, mid)
}
}
// 根据循环不变量,left-1 现在是最大的回答为「是」的数
return left - 1;
}
};
作者:灵茶山艾府
链接:https://leetcode.cn/problems/h-index-ii/solutions/2504326/tu-jie-yi-tu-zhang-wo-er-fen-da-an-si-ch-d15k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.3左开右闭区间【left(包括left)左边的点均成立,right右边的点均不成立】
while (left < right)
当到达left = right - 1时
如果依旧使用向下取整
int mid = (left + right) / 2; 使得mid == left;
这里只有一种情况:
1.该mid是成立,left = mid(left);left = right - 1;
这里的left依旧等于right - 1,发生无限循环,冲突
2.此时由于mid = left, 而根据左开得知,left指向的点必是成立的,所以mid没有不成立的情况。
如果我们使用向上取整呢?
使用向下取整int mid = (left + right + 1) / 2; 使得mid == right;
这里也有两种情况:
1.该mid是成立,left = mid(right);
而又因为左开区间,left(包括left)左侧所有点均成立均是成立的
由于这里右闭区间,表明包括从right右边所有的点是不成立的
故h = left 或 right;
2.该mid是不成立,right = mid(right) - 1 = left;
而又因为左开区间,left(包括left)左侧所有点均成立均是成立的
由于这里右闭区间,表明包括从right右边所有的点是不成立的
故h = left 或 right;
补充:且注意这里由于左闭右开,所以可从(0,n] (选用0就保证了1在区间内)作为初始区间,至于为何是从1而不是0开始,请看下方解释。
假设最后的mid = 3,下图分别对应两种情况最终位置:
//左开右闭区间
class Solution {
public:
int hIndex(vector<int> &citations) {
// 在区间 (left, right] 内询问
int n = citations.size();
int left = 0;
int right = n;
while (left < right) { // 区间不为空
// 循环不变量:
// left 的回答一定为「是」
// right+1 的回答一定为「否」
int mid = (left + right + 1) / 2; // 保证 mid 在二分区间内
// 引用次数最多的 mid 篇论文,引用次数均 >= mid
if (citations[n - mid] >= mid) {
left = mid; // 询问范围缩小到 (mid, right]
} else {
right = mid - 1; // 询问范围缩小到 (left, mid-1]
}
}
// 根据循环不变量,left 现在是最大的回答为「是」的数
return left;
}
};
作者:灵茶山艾府
链接:https://leetcode.cn/problems/h-index-ii/solutions/2504326/tu-jie-yi-tu-zhang-wo-er-fen-da-an-si-ch-d15k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.4开区间【left(包括left)左边的点均成立,right(包括right)右边的点均不成立】
while (left + 1 < right) [(3,5)包含4,(3,4)区间内只有小数]
当left = right - 2;时, 有mid == left + 1;
这里也有两种情况
1.该mid是成立,left = mid(left + 1) = right - 1;
而又因为左开区间,left(包含left)左侧所有点均成立均是成立的
由于这里右开区间,right(包括right)右侧所有的点是不成立的
故h = (left)或 (right - 1);
2.该mid是不成立,right = mid(right - 1);
此时 left == right - 1;
由于这里左开区间,所以left(包括left)左边所有的点均成立。
由于这里右开区间,所以right(包括right)右边所有的点均不成立。
故h = (left)或 (right - 1);
假设最后的mid = 3,下图分别对应两种情况最终位置:
//开区间
class Solution {
public:
int hIndex(vector<int> &citations) {
// 在区间 (left, right) 内询问
int n = citations.size();
int left = 0;
int right = n + 1;
while (left + 1 < right) { // 区间不为空
// 循环不变量:
// left 的回答一定为「是」
// right 的回答一定为「否」
int mid = (left + right) / 2;
// 引用次数最多的 mid 篇论文,引用次数均 >= mid
if (citations[n - mid] >= mid) {
left = mid; // 询问范围缩小到 (mid, right)
} else {
right = mid; // 询问范围缩小到 (left, mid)
}
}
// 根据循环不变量,left 现在是最大的回答为「是」的数
return left;
}
};
作者:灵茶山艾府
链接:https://leetcode.cn/problems/h-index-ii/solutions/2504326/tu-jie-yi-tu-zhang-wo-er-fen-da-an-si-ch-d15k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
4.为何从1开始
肯定有0篇论文的引用次数≥0,
对于讠=0的询问,回答必然为「是」
因此二分区间不需要包含0,可以在「1,n」/ (0,n] 中二分。
5.各种情况图览
275.H指数II的更多相关文章
- Leetcode之二分法专题-275. H指数 II(H-Index II)
Leetcode之二分法专题-275. H指数 II(H-Index II) 给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照升序排列.编写一个方法,计算出研究者的 h 指数. ...
- Java实现 LeetCode 275 H指数 II
275. H指数 II 给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照升序排列.编写一个方法,计算出研究者的 h 指数. h 指数的定义: "h 代表"高 ...
- 275 H-Index II H指数 II
这是 H指数 进阶问题:如果citations 是升序的会怎样?你可以优化你的算法吗? 详见:https://leetcode.com/problems/h-index-ii/description/ ...
- [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 ...
- [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 ...
- 275. H 指数 II--Leetcode_暴力
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/h-index-ii 著作权归领扣网络所有.商业转载请联系官方授权,非商业转载请注明出处. 题目的大意是 ...
- 275. H 指数 II--Leetcode_二分
来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/h-index-ii 著作权归领扣网络所有.商业转载请联系官方授权,非商业转载请注明出处. 题目的大意是 ...
- [LeetCode] 274. H-Index H指数
Given an array of citations (each citation is a non-negative integer) of a researcher, write a funct ...
- [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 ...
随机推荐
- Python——第二章:字符串操作——索引和切片
索引: 按照位置提取元素 可以采用索引的方式来提取某一个字符(文字) s = "我叫周杰伦" print(s[3]) #程序员都是从0开始数,这里的3代表第4位,也就是" ...
- 6.elasticsearch中search template和alias
什么是search template 顾名思义,查询模版,就是提前设定好查询的DSL,再次查询时,只需要指定对应的模版,然后传入相应的参数就好.一是可以每次不用构建复杂的DSL,二是可以供开发直接使用 ...
- thymeleaf特殊字符输出转义
thymeleaf特殊字符输出转义,字符串包括/@#¥%&*,正常来说他们输出会被转义掉.主要是使用了th:inline="javascript"标签,它会自动安全转义字符 ...
- 2021-01-20:mysql中,一张表里有3亿数据,未分表,要求是在这个大表里添加一列数据。数据库不能停,并且还有增删改操作。请问如何操作?
2021-01-20:mysql中,一张表里有3亿数据,未分表,要求是在这个大表里添加一列数据.数据库不能停,并且还有增删改操作.请问如何操作? 福哥答案2020-01-20: 陌陌答案: 用pt_o ...
- 如何使用GaussDB(DWS)的本地临时表进行数据处理
本文分享自华为云社区<GaussDB(DWS)临时表系列 - 本地临时表>,作者: acydy . GaussDB(DWS) 从8.2.1版本后支持三种形式的临时表:本地临时表.Volat ...
- Materialize MySQL引擎:MySQL到Click House的高速公路
摘要: MySQL到ClickHouse数据同步原理及实践 引言 熟悉MySQL的朋友应该都知道,MySQL集群主从间数据同步机制十分完善.令人惊喜的是,ClickHouse作为近年来炙手可热的大数据 ...
- RDS:一致性处理事务的神器
摘要:RDS关系型数据库是一种基于云计算平台的即开即用.稳定可靠.弹性伸缩.便捷管理的在线关系型数据库服务. 本文分享自华为云社区<一致性处理事务这下还是看RDS的吧[秋招特训]>,作者: ...
- 云小课 | 华为云KYON之VPC终端节点
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:在华为云KYON( ...
- mysql新增数据库新增用户并授权用户
-- 创建数据库CREATE DATABASE baseName; -- 创建用户CREATE USER 'userName' @ '访问限制' IDENTIFIED BY 'password'; ...
- 在毫秒量级上做到“更快”!DataTester 助力飞书提升页面秒开率
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 用户体验是决定互联网产品能否长久生存的基础,每一个基于产品功能.使用.外观的微小体验,都将极大关系到用户留存影响. ...