一、题目分析
(一)问题描述
给定一个整数数组 citations,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。我们需要计算并返回该研究者的 H 指数。根据维基百科定义:H 指数代表"高引用次数",一名科研人员的 H 指数是指他(她)至少发表了 h 篇论文,并且至少有 h 篇论文被引用次数大于等于 h 。如果有多个可能的 h 值,取最大的那个。
比如,若有论文引用次数数组为 [3,0,6,1,5],经过计算其 H 指数是 3,因为有 3 篇论文(引用次数为 3、6、5 )的引用次数大于等于 3 。
(二)核心目标
遍历处理论文引用次数数组,找到最大的 h 值,满足存在至少 h 篇论文的引用次数 ≥ h 。
二、算法思想:排序 + 线性遍历(贪心思想的体现)
(一)排序的作用
首先对 citations 数组进行排序(升序排序 )。排序之后,数组的顺序能够帮助我们更方便地找到符合 H 指数定义的那个 h 值。升序排序后,数组后面的元素值相对较大,我们可以从后往前或者从前往后,利用数组的有序性来判断满足条件的 h 。
(二)线性遍历与贪心策略
排序完成后,进行线性遍历。我们的贪心策略是:从数组的一端开始,寻找满足"存在 h 篇论文引用次数 ≥ h"这一条件的最大 h 。具体来说,对排序后的数组,我们从前往后遍历,对于索引 i ,考虑以 n - i 作为可能的 h 值(n 是数组长度,也就是论文的总篇数 ),判断当前论文的引用次数 citations[i] 是否大于等于 n - i 。一旦找到第一个满足该条件的 i ,那么 n - i 就是我们要找的最大 H 指数。这是因为数组是升序排列的,后面的元素值更大,当在某个位置满足条件后,继续往后遍历得到的 n - i 会更小,所以第一个满足条件的 n - i 就是最大的符合要求的 H 指数,这体现了贪心算法中"找到第一个满足局部条件就能得到全局最优"的思想 。
三、代码实现及详细注释
java
import java.util.Arrays;
class Solution {
public int hIndex(int[] citations) {
// 第一步:对引用次数数组进行升序排序
Arrays.sort(citations);
int n = citations.length; // 获取论文的总篇数,也就是数组的长度
for (int i = 0; i < n; i++) {
// 对于当前索引i,考虑h值为n - i。这里的含义是:
// 假设h是n - i,那么需要至少有n - i篇论文的引用次数≥h
// 由于数组是升序排序的,citations[i]及后面的元素是较大的,当citations[i] >= n - i时,
// 说明从i到n - 1位置的这n - i篇论文的引用次数都 >= citations[i](因为升序),也就 >= n - i
if (citations[i] >= n - i) {
return n - i; // 找到满足条件的最大h值,直接返回
}
}
return 0; // 如果遍历完都没有满足条件的,说明H指数为0(比如所有论文引用次数都为0的情况 )
}
}
(一)代码执行流程详解
- 排序操作:
java
Arrays.sort(citations);
这行代码使用 Java 内置的 Arrays.sort 方法对 citations 数组进行升序排序。例如,对于输入数组 [3,0,6,1,5],排序后会变成 [0,1,3,5,6] 。排序的时间复杂度为 O(nlogn)O(n\log n)O(nlogn) ,其中 n 是数组的长度,这是由排序算法的时间复杂度决定的(Java 中 Arrays.sort 对于基本数据类型数组采用的是优化的快速排序等算法,平均时间复杂度为 O(nlogn)O(n\log n)O(nlogn) )。
- 遍历判断过程:
java
int n = citations.length;
for (int i = 0; i < n; i++) {
if (citations[i] >= n - i) {
return n - i;
}
}
- 首先获取数组长度 n ,代表论文的总篇数。
- 然后进入循环遍历,i 从 0 开始递增到 n - 1 。对于每一个 i ,计算 n - i ,这个值代表的是假设的 h 值,含义是当前考虑有 n - i 篇论文可能满足引用次数 ≥ n - i 。
- 因为数组是升序排序的,citations[i] 是第 i + 1 小的引用次数(数组索引从 0 开始 ),当 citations[i] >= n - i 时,说明从第 i 篇论文开始(包括第 i 篇 ),后面一共有 n - i 篇论文(索引从 i 到 n - 1 ),它们的引用次数都大于等于 citations[i] ,自然也大于等于 n - i (因为 citations[i] >= n - i 且数组升序 )。所以此时 n - i 就是满足条件的 H 指数,直接返回即可。这是因为我们是从前往后遍历,一旦找到第一个满足条件的 i ,对应的 n - i 就是最大的可能的 H 指数。比如排序后的数组 [0,1,3,5,6] ,n = 5 ,当 i = 2 时,n - i = 3 ,citations[2] = 3 ,满足 citations[i] >= n - i ,所以返回 3 ,也就是正确的 H 指数。
- 返回默认值:
java
return 0;
如果循环遍历结束后,没有找到满足 citations[i] >= n - i 的情况,说明所有论文的引用次数都非常低,比如数组全为 0 ,此时 H 指数为 0 ,所以返回 0 。
四、算法的时间复杂度和空间复杂度分析
(一)时间复杂度
- 排序操作的时间复杂度:使用 Arrays.sort 对数组进行排序,其时间复杂度为 O(nlogn)O(n\log n)O(nlogn) ,其中 n 是数组 citations 的长度。
- 线性遍历的时间复杂度:排序后进行的线性遍历,时间复杂度为 O(n)O(n)O(n) ,n 同样是数组长度。
所以,整个算法的时间复杂度由排序操作主导,为 O(nlogn)O(n\log n)O(nlogn) 。
(二)空间复杂度
- 排序操作在 Java 中,对于基本数据类型数组的 Arrays.sort 方法,采用的是原地排序(大部分情况下 ),不需要额外的大量空间,空间复杂度为 O(logn)O(\log n)O(logn) (主要是排序过程中递归调用栈或者用于辅助排序的空间,基于快速排序等算法的实现 )。
- 其他变量如 n 、i 等都是常数级别的空间占用。
所以,整个算法的空间复杂度为 O(logn)O(\log n)O(logn) (主要由排序操作决定 ),在大多数情况下可以认为是较为高效的空间利用。