题目描述
给定一个整数数组 citations,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 H 指数。
H 指数 的定义 (来自维基百科): 一位科研人员的 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
提示:
n == citations.length1 <= n <= 50000 <= citations[i] <= 1000
算法思路
核心思想:计数 + 逆序遍历
要计算 H 指数,最核心的是要找到满足定义的最大 h 值。 一个直观的思路是,我们可以尝试不同的 h 值,然后验证是否满足 H 指数的定义。 为了高效地验证,我们可以使用 计数排序 的思想,统计出不同引用次数的论文数量,然后逆序遍历可能的 h 值,快速找到符合条件的最大值。
思考过程:
-
理解 H 指数的定义: 至少有
h篇论文被引用至少h次。 例如,如果 H 指数为 3,意味着至少有 3 篇论文引用次数 ≥ 3。 -
确定
h值的范围: H 指数h的最大可能值是多少? 如果一个研究者发表了n篇论文,那么 H 指数h最大不会超过n。因此,h的取值范围是[0, n]。 -
验证
h值: 对于给定的h值是否满足条件? 需要统计引用次数 >=h的论文数量,如果这个数量 >=h,则说明当前的h值是有效的。 -
高效统计引用次数: 为了快速统计引用次数,可以使用计数排序的思想。创建一个计数数组
papers,其中papers[i]存储的是引用次数为i的论文数量。 遍历citations数组,将每篇论文的引用次数c映射到papers数组中进行计数。 为了简化处理,可以将引用次数超过论文总数n的论文都归类到papers[n]中,因为 H 指数最大不会超过n。这样的计数方式适合n较小的情况下使用。 -
逆序遍历找最大
h: 由于 H 指数是满足条件的最大值,我们可以从最大的可能值n开始,逆序遍历h的可能取值[n, n-1, n-2, ..., 0]。 对于每个h值,统计引用次数大于等于h的论文总数。由于是逆序计算,第一次遇到满足 “引用次数大于等于h的论文总数 ≥h” 的h值,那么这个h值就是我们要找的最大 H 指数,直接返回即可。 -
处理没有论文的情况: 如果论文数组为空,或者没有找到任何满足条件的
h值,则 H 指数为 0。
总结算法思路:
- 初始化:
n = len(citations)(获取论文总数)。papers = make([]int, n+1)(创建计数数组papers,用于统计不同引用次数的论文数量)。
- 计数引用次数: 遍历
citations数组,统计每种引用次数的论文数量,并将计数结果存储在papers数组中。 对于引用次数超过n的论文,计数在papers[n]中。 - 逆序遍历并验证
h值: 从h = n逆序遍历到h = 0:- 累加
papers数组中引用次数大于等于当前h值的论文数量 (count)。 - 如果
count >= h,则说明当前h值满足 H 指数的定义,直接返回h。
- 累加
- 返回 0: 如果循环结束都没有找到满足条件的
h值,则返回 0 (表示 H 指数为 0)。
复杂度分析
- 时间复杂度: O(n),其中 n 是
citations数组的长度。 主要的时间消耗在遍历citations数组进行计数,以及逆序遍历papers数组。 两个遍历都是线性时间复杂度。 - 空间复杂度: O(n),主要的空间消耗在创建计数数组
papers,其长度为n+1,与论文数量n成正比。
代码实现
func hIndex(citations []int) int {
n := len(citations) // 最大的 h 值不会超过 n
papers := make([]int, n+1) // papers[i] 表示引用次数为 i 的论文数量
// 统计各引用次数的论文数量
for _, c := range citations {
if c >= n {
papers[n]++ // 引用次数超过 n 的都算作 n
} else {
papers[c]++
}
}
count := 0
for i := n; i >= 0; i-- {
count += papers[i] // 引用次数大于等于 i 的论文总数
if count >= i { // 找到满足 h 指数定义的 h 值
return i
}
}
return 0 // 如果没有论文,则 H 指数为 0
}
示例解析
示例 1: citations = [3,0,6,1,5]
- 初始化:
n = 5,papers = [0, 0, 0, 0, 0, 0](长度为 6) - 计数引用次数:
citations[0] = 3,papers[3]++,papers = [0, 0, 0, 1, 0, 0]citations[1] = 0,papers[0]++,papers = [1, 0, 0, 1, 0, 0]citations[2] = 6,citations[2] >= n,papers[5]++,papers = [1, 0, 0, 1, 0, 1]citations[3] = 1,papers[1]++,papers = [1, 1, 0, 1, 0, 1]citations[4] = 5,citations[4] >= n,papers[5]++,papers = [1, 1, 0, 1, 0, 2]
- 逆序遍历并验证
h值:- i = 5:
count = papers[5] = 2。count < i(2 < 5),不满足条件。 - i = 4:
count = papers[5] + papers[4] = 2 + 0 = 2。count < i(2 < 4),不满足条件。 - i = 3:
count = papers[5] + papers[4] + papers[3] = 2 + 0 + 1 = 3。count >= i(3 >= 3),满足条件,返回h = 3。
- i = 5:
示例 2: citations = [1,3,1]
- 初始化:
n = 3,papers = [0, 0, 0, 0](长度为 4) - 计数引用次数:
papers = [0, 2, 0, 1](计算过程略) - 逆序遍历并验证
h值:- i = 3:
count = papers[3] = 1。count < i(1 < 3),不满足条件。 - i = 2:
count = papers[3] + papers[2] = 1 + 0 = 1。count < i(1 < 2),不满足条件。 - i = 1:
count = papers[3] + papers[2] + papers[1] = 1 + 0 + 2 = 3。count >= i(3 >= 1),满足条件,返回h = 1。
- i = 3:
关键点
- H 指数的定义理解: 理解 H 指数的定义。
- 计数排序思想: 使用计数数组降低复杂度。
- 逆序遍历: 逆序遍历
h的可能取值,能够快速找到最大的满足条件的 H 指数。 - 边界处理: 处理引用次数超过论文总数的情况,以及没有论文的情况。