LeetCode 274. H 指数:两种高效解法全解析

在科研成果评价领域,H 指数是一个非常经典的指标,而 LeetCode 274 题正是围绕 H 指数的计算展开。这道题看似简单,但背后藏着两种思路迥异的高效解法。今天我们就来深入剖析这道题,把两种解法的逻辑、实现和优劣讲透。

一、题目回顾与 H 指数定义

首先明确题目要求:给定一个整数数组 citations,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数,计算并返回该研究者的 H 指数。

核心是理解 H 指数的定义(划重点):一名科研人员的 H 指数是指他至少发表了 h 篇论文,并且这 h 篇论文每篇的被引用次数都大于等于 h。如果存在多个可能的 h 值,取最大的那个。

举个例子帮助理解:若 citations = [1,3,1],H 指数是 1。因为研究者有 3 篇论文,其中至少 1 篇被引用 ≥1 次,而要达到 h=2 则需要至少 2 篇论文被引用 ≥2 次(实际只有 1 篇3次,不满足),所以最大的 h 是 1。

二、解法一:计数排序思路(时间 O(n),空间 O(n))

先看第一种解法的代码,这是一种基于计数排序的优化方案,适合对时间效率要求较高的场景。

typescript 复制代码
function hIndex_1(citations: number[]): number {
  const ciLen = citations.length;
  const count = new Array(ciLen + 1).fill(0);
  for (let i = 0; i < ciLen; i++) {
    if (citations[i] > ciLen) {
      count[ciLen]++;
    } else {
      count[citations[i]]++;
    }
  }
  let total = 0;
  for (let i = ciLen; i >= 0; i--) {
    total += count[i];
    if (total >= i) {
      return i;
    }
  }
  return 0;
};

2.1 核心思路

H 指数的最大值不可能超过论文总数 n(因为要至少 h 篇论文,h 最多等于论文数)。所以对于引用次数超过 n 的论文,我们可以统一视为引用次数为 n(不影响 H 指数的计算)。

基于这个特点,我们可以用一个计数数组 count 统计每个引用次数(0 到 n)对应的论文数量,然后从后往前累加计数,找到第一个满足「累加总数 ≥ 当前引用次数」的数值,这个数值就是最大的 H 指数。

2.2 步骤拆解(以 citations = [3,0,6,1,5] 为例)

  1. 初始化变量 :论文总数 ciLen = 5,计数数组 count 长度为 ciLen + 1 = 6,初始值全为 0(count = [0,0,0,0,0,0])。

  2. 统计引用次数分布 :遍历 citations 数组,将每篇论文的引用次数映射到 count 中:

    go 复制代码
     最终`count` 含义:引用 0 次的 1 篇、1 次的 1 篇、3 次的 1 篇、5 次及以上的 2 篇。
    • 3 ≤ 5 → count[3]++ → count = [0,0,0,1,0,0]

    • 0 ≤ 5 → count[0]++ → count = [1,0,0,1,0,0]

    • 6 > 5 → count[5]++ → count = [1,0,0,1,0,1]

    • 1 ≤ 5 → count[1]++ → count = [1,1,0,1,0,1]

    • 5 ≤ 5 → count[5]++ → count = [1,1,0,1,0,2]

  3. 倒序累加找 H 指数 :从最大可能的 h(即 ciLen=5)开始,累加 count[i](表示引用次数 ≥i 的论文总数),直到累加和 ≥i:

    • i=5:total = 0 + 2 = 2 → 2 < 5 → 继续

    • i=4:total = 2 + 0 = 2 → 2 < 4 → 继续

    • i=3:total = 2 + 1 = 3 → 3 ≥ 3 → 满足条件,返回 3

最终结果为 3,符合预期(3 篇论文被引用 ≥3 次:3、6、5)。

2.3 优缺点

优点:时间复杂度 O(n),只需要两次遍历数组,效率极高;空间复杂度 O(n),仅需一个固定长度的计数数组。

缺点:需要额外的空间存储计数数组,对于论文数量极少的场景,空间开销不明显,但思路相对排序法更难理解。

三、解法二:排序思路(时间 O(n log n),空间 O(1))

第二种解法是基于排序的思路,逻辑更直观,容易理解,也是很多人首先会想到的方案。

typescript 复制代码
function hIndex(citations: number[]): number {
  // 思路:逆序排序
  citations.sort((a, b) => b - a);
  let res = 0;
  for (let i = 0; i < citations.length; i++) {
    if (citations[i] >= i + 1) {
      res = i + 1;
    }
  }
  return res;
};

3.1 核心思路

将引用次数数组逆序排序 (从大到小),此时排序后的数组第 i 个元素(索引从 0 开始)表示第 i+1 篇论文的引用次数。如果该元素 ≥ i+1,说明前 i+1 篇论文的引用次数都 ≥ i+1,此时 H 指数至少为 i+1。遍历完数组后,最大的这个 i+1 就是最终的 H 指数。

3.2 步骤拆解(同样以 citations = [3,0,6,1,5] 为例)

  1. 逆序排序数组 :排序后 citations = [6,5,3,1,0]

  2. 遍历数组找最大 h :初始化 res = 0,依次判断每个元素:

    • i=0:citations[0] = 6 ≥ 0+1=1 → res = 1

    • i=1:citations[1] = 5 ≥ 1+1=2 → res = 2

    • i=2:citations[2] = 3 ≥ 2+1=3 → res = 3

    • i=3:citations[3] = 1 ≥ 3+1=4 → 不满足,res 不变

    • i=4:citations[4] = 0 ≥ 4+1=5 → 不满足,res 不变

  3. 返回结果:最终 res = 3,与解法一结果一致。

3.3 优缺点

优点:逻辑直观,容易理解和实现;空间复杂度低,若允许原地排序(如 JavaScript 的 sort 方法),空间复杂度为 O(log n)(排序的递归栈空间),否则为 O(1)。

缺点:时间复杂度由排序决定,为 O(n log n),对于大规模数据(如论文数量极多),效率不如解法一。

四、两种解法对比与适用场景

解法 时间复杂度 空间复杂度 核心优势 适用场景
计数排序法 O(n) O(n) 时间效率极高,两次线性遍历 大规模数据,对时间要求高
逆序排序法 O(n log n) O(1) 逻辑直观,空间开销小 小规模数据,追求代码简洁易读

五、常见易错点提醒

  1. 混淆 H 指数的定义:容易把「至少 h 篇论文 ≥h 次」写成「h 篇论文 exactly h 次」,导致判断条件错误(如之前有同学把解法一的 total ≥ i 写成 total === i)。

  2. 排序方向错误:解法二必须逆序排序(从大到小),若正序排序会导致逻辑混乱,无法正确统计。

  3. 忽略边界情况:如 citations = [0](H 指数 0)、citations = [100](H 指数 1),需确保两种解法都能覆盖这些场景。

六、总结

LeetCode 274 题的两种解法各有优劣:计数排序法以空间换时间,适合大规模数据;逆序排序法逻辑简洁,适合小规模数据。理解这两种解法的核心在于吃透 H 指数的定义------「至少 h 篇论文 ≥h 次引用」,所有的逻辑都是围绕这个定义展开的。

建议大家在练习时,先尝试自己实现逆序排序法(容易上手),再深入理解计数排序法的优化思路,通过对比两种解法的差异,加深对「时间复杂度」和「空间复杂度」权衡的理解。

相关推荐
jonjia8 小时前
模块、脚本与声明文件
typescript
jonjia8 小时前
配置 TypeScript
typescript
jonjia8 小时前
TypeScript 工具函数开发
typescript
jonjia8 小时前
注解与断言
typescript
jonjia8 小时前
IDE 超能力
typescript
jonjia8 小时前
对象类型
typescript
jonjia8 小时前
快速搭建 TypeScript 开发环境
typescript
jonjia8 小时前
TypeScript 的奇怪之处
typescript
jonjia8 小时前
类型派生
typescript
jonjia8 小时前
开发流程中的 TypeScript
typescript