h指数
给你一个整数数组 citations
,其中 citations[i]
表示研究者的第 i
篇论文被引用的次数。计算并返回该研究者的 h
指数。
根据维基百科上 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
实现思路:
要计算一个研究者的 h 指数,你可以按照以下步骤进行:
-
排序:将论文引用次数从高到低进行排序。
-
计算 h 指数:从排序后的数组的最后一个元素(即引用次数最高的论文)开始,向前遍历数组:
- 维护一个计数器
h
,初始值为 1。 - 对于数组中的每个元素
citations[i]
:- 如果
citations[i]
大于或等于h
,则h
加 1。 - 如果
citations[i]
小于h
,则停止增加h
,因为此时已经不能满足 h 指数的定义(即每篇论文至少被引用 h 次)。
- 如果
- 维护一个计数器
-
返回 h 指数 :返回计算出的
h
值。
思路模拟:以citations = [3,0,6,1,5]为例(大家可以在图纸上画一遍模拟一下)
首先我们进行从高到低排序得到 6,5,3,1,0.然后从数组最后往前开始遍历数组。判断citations[i]是否大于等于h
第一次遍历:0 (数组元素)< 1(h的值),因此不满足条件,结束当前遍历。
第二次遍历:1 = 1,满足条件,h++,进行下一次遍历。
第三次遍历:3 > 2 满足条件,h++,进行下一次遍历。
第四次遍历:5 > 3 满足条件,h++,进行下一次遍历。
第五次遍历: 6 > 4 满足条件,h++,循环结束。
然后因为我们h是从1开始,最后一次满足条件时多加了一次,在我们返回时则需要减1.
实现代码:
java
public int hIndex(int[] citations) {
//数组从高到低进行排序 Arrays.sort函数
Arrays.sort(citations);
int h=1; //定义h指数
//对数组进行遍历,从后往前,如果数组元素大于等于h,则为h指数,h++
for (int i = citations.length-1; i >=0 ; i--) {
if (citations[i] >= h){
h++;
}else {
break;
}
}
return h-1;//因最后一次符合条件的判断多加了一次,因此需要减一
}
实现思路二:使用一个计数数组来统计每个引用次数的论文数量,然后通过反向迭代来确定 h 指数。
实现代码:
java
public int hIndex(int[] citations) {
int n = citations.length; // 1. 获取输入数组 citations 的长度。
int[] count = new int[n + 1]; // 2. 创建一个长度为 n + 1 的数组 count,用于统计每个引用次数的论文数量。数组索引从 0 到 n,其中索引 i 表示引用次数为 (i + 1) 的论文数量。
for (int num : citations) { // 3. 遍历数组 citations 中的每个引用次数 num。
count[Math.min(num, n)]++; // 4. 对于每个引用次数 num,如果 num 大于 n,则它将与 n 一样被视为 n。然后,对应索引的 count 数值增加 1。
}
int total = 0; // 5. 初始化总论文数量计数器 total 为 0,用于计算至少被引用特定次数的论文总数。
for (int i = n; ; i--) { // 6. 开始一个无限循环,从 n 开始递减至 0。循环条件为空,表示无限迭代,但内部的逻辑将决定何时退出循环。
total += count[i]; // 7. 将当前索引 i 对应的引用次数的论文数量加到 total 上。这表示统计了至少被引用 i 次的论文数量。
if (total >= i) { // 8. 如果 total 大于等于当前的索引 i,根据 h 指数的定义,我们找到了满足条件的最小 h 值。
return i; // 9. 返回索引 i 作为 h 指数的值。此时,我们知道至少有 i 篇论文被引用了至少 i 次。
}
}
}
代码逻辑的核心在于:
- 使用
count
数组来统计每个引用次数的论文数量,数组索引i
表示引用次数为(i + 1)
的论文数量。 - 通过反向迭代
count
数组,累加至少被引用特定次数的论文总数。 - 当累加的论文总数
total
大于等于当前的引用次数i
时,满足 h 指数的定义,返回当前的i
作为 h 指数。
这种方法的时间复杂度是 O(n),其中 n 是数组 citations
的长度,因为它只需要对数组进行两次遍历:一次用于统计引用次数,一次用于计算 h 指数。这种方法避免了对原始数组进行排序,因此在某些情况下可能更高效。
知识补充:
1.Arrays.sort()函数
Arrays.sort()
是 Java 中的一个方法调用,用于对整数数组 进行排序。这个方法是基于 TimSort 算法实现的,TimSort 是一种结合了归并排序和插入排序的高效排序算法,它在多种情况下都能提供很好的性能。(关于归并排序和插入排序如果有需要,后期我出两期内容)
以下是关于 Arrays.sort()
方法的一些关键点:
-
原地排序 :
Arrays.sort()
对数组进行原地排序,意味着它直接修改传入的数组,而不是创建一个新的排序数组。 -
时间复杂度 :对于大多数情况,
Arrays.sort()
的平均时间复杂度是 O(n log n),其中 n 是数组的长度。 -
稳定性 :
Arrays.sort()
是一个稳定的排序算法,这意味着相等的元素在排序后保持它们原始的顺序。 -
使用场景:当你需要对数组中的元素进行排序时,可以使用这个方法。例如,你可以对整数、浮点数、对象数组等进行排序。
-
重载方法 :
Arrays.sort()
提供了多个重载版本,支持不同类型的数组排序,包括原始数据类型数组(如 int、float 等)和对象数组。 -
自定义排序 :对于对象数组,你可以传递一个
Comparator
实例作为参数,以自定义排序逻辑。 -
并行排序 :从 Java 8 开始,
Arrays.sort()
默认使用并行排序(如果底层的硬件支持),这可以提高大数据集的排序速度。 -
异常处理 :如果数组包含
null
或排序器(Comparator
)抛出异常,则Arrays.sort()
会抛出NullPointerException
或其他相关异常。