摘要
在科研评价体系中,H 指数 是衡量研究者影响力的经典指标。而在算法面试中,H 指数的衍生题 H 指数 II,要求我们在 排序数组上以对数时间复杂度 解题。这不仅是一个算法挑战,更是对你二分查找理解深度的考验。
本文将带你用 Swift 实现 LeetCode 275 题的完整解法,通过二分查找优雅地求出 H 指数,并延展讲解它在实际场景中的应用,如科研平台、学术推荐系统等。
描述
你得到一个按照非降序排序的数组 citations
,其中 citations[i]
表示一篇论文被引用的次数。我们要计算出该研究者的 H 指数。
H 指数定义如下:
H 指数是指一个研究者至少有
h
篇论文被引用了至少h
次,其余的论文引用数不超过h
。
这道题的特别之处在于它对效率的要求——我们需要实现一个 对数时间复杂度的解法,也就是说时间复杂度应该是 O(log n)
。
题解答案
由于数组已经排好序,我们可以使用经典的二分查找来解决问题。二分查找的核心思路是:
-
从中间值开始,查看当前引用数是否满足 “剩余论文 ≥ 当前引用次数” 的条件。
-
如果满足,说明可能还有更高的 h 指数,在左边继续找;
-
如果不满足,说明当前引用数太小,向右边查找。
题解代码分析
func hIndex(_ citations: [Int]) -> Int {
let n = citations.count
var left = 0
var right = n - 1
while left <= right {
let mid = left + (right - left) / 2
let h = n - mid
if citations[mid] == h {
return h
} else if citations[mid] < h {
left = mid + 1
} else {
right = mid - 1
}
}
return n - left
}
分步解析:
-
let n = citations.count
:获取论文总数。 -
mid = (left + right) / 2
:计算二分查找的中间位置。 -
h = n - mid
:此时剩下的n - mid
篇论文是被引用次数大于等于citations[mid]
的论文数。 -
if citations[mid] == h
:刚好符合 H 指数定义,直接返回。 -
如果
citations[mid] < h
,说明当前引用数太小,H 指数可能更大,需要向右查找。 -
如果
citations[mid] > h
,引用数太大,不满足条件,需要往左查找。
最后,当跳出循环时,left
会是第一个不满足条件的位置,H 指数为 n - left
。
示例测试及结果
示例 1
print(hIndex([0, 1, 3, 5, 6])) // 输出:3
解释:3 篇论文至少被引用 3 次,符合 H 指数定义。
示例 2
print(hIndex([1, 2, 100])) // 输出:2
解释:2 篇论文分别被引用 2 和 100 次,满足至少 2 篇被引用 ≥2 次。
示例 3
print(hIndex([0, 0, 0])) // 输出:0
解释:没有任何论文被引用,H 指数为 0。
时间复杂度
-
使用了 二分查找,每次查询缩小一半范围;
-
时间复杂度:O(log n),完全满足题目要求的对数级别效率。
空间复杂度
-
除了少量变量,算法没有额外开辟存储空间;
-
空间复杂度:O(1)。
总结
通过这道题,我们学到的不仅仅是如何计算 H 指数,更重要的是在“已排序数组”上快速查找“最大最小值边界”的二分查找技巧。
在现实应用中,类似场景还有:
-
学术平台中对研究者影响力的定量分析;
-
推荐算法中,基于 H 指数对内容优先级排序;
-
甚至在新闻舆情、知识问答系统中,可以用类似机制评估信息热度。
延伸思考
-
如果输入不是排序好的数组,如何用线性时间计算 H 指数?
-
如果允许一定程度的引用浮动(比如 ±1),如何设计更加稳健的 H 指数计算逻辑?
-
在并发或分布式系统中,能否做出近似的快速估算模型?
这些问题正是 H 指数在工程落地中会遇到的挑战,也是你从刷题走向工程化思维的关键一步。