面试经典算法150题系列-h指数

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 指数,你可以按照以下步骤进行:

  1. 排序:将论文引用次数从高到低进行排序。

  2. 计算 h 指数:从排序后的数组的最后一个元素(即引用次数最高的论文)开始,向前遍历数组:

    • 维护一个计数器 h,初始值为 1。
    • 对于数组中的每个元素 citations[i]
      • 如果 citations[i] 大于或等于 h,则 h 加 1。
      • 如果 citations[i] 小于 h,则停止增加 h,因为此时已经不能满足 h 指数的定义(即每篇论文至少被引用 h 次)。
  3. 返回 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() 方法的一些关键点:

  1. 原地排序Arrays.sort() 对数组进行原地排序,意味着它直接修改传入的数组,而不是创建一个新的排序数组。

  2. 时间复杂度 :对于大多数情况,Arrays.sort() 的平均时间复杂度是 O(n log n),其中 n 是数组的长度。

  3. 稳定性Arrays.sort() 是一个稳定的排序算法,这意味着相等的元素在排序后保持它们原始的顺序。

  4. 使用场景:当你需要对数组中的元素进行排序时,可以使用这个方法。例如,你可以对整数、浮点数、对象数组等进行排序。

  5. 重载方法Arrays.sort() 提供了多个重载版本,支持不同类型的数组排序,包括原始数据类型数组(如 int、float 等)和对象数组。

  6. 自定义排序 :对于对象数组,你可以传递一个 Comparator 实例作为参数,以自定义排序逻辑。

  7. 并行排序 :从 Java 8 开始,Arrays.sort() 默认使用并行排序(如果底层的硬件支持),这可以提高大数据集的排序速度。

  8. 异常处理 :如果数组包含 null 或排序器(Comparator)抛出异常,则 Arrays.sort() 会抛出 NullPointerException 或其他相关异常。

相关推荐
熬夜有啥好5 小时前
数据结构——哈希表
数据结构·散列表
琹箐5 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
renhongxia16 小时前
如何基于知识图谱进行故障原因、事故原因推理,需要用到哪些算法
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·知识图谱
坚持就完事了6 小时前
数据结构之树(Java实现)
java·算法
算法备案代理6 小时前
大模型备案与算法备案,企业该如何选择?
人工智能·算法·大模型·算法备案
赛姐在努力.6 小时前
【拓扑排序】-- 算法原理讲解,及实现拓扑排序,附赠热门例题
java·算法·图论
我能坚持多久7 小时前
【初阶数据结构01】——顺序表专题
数据结构
野犬寒鸦7 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
霖霖总总8 小时前
[小技巧66]当自增主键耗尽:MySQL 主键溢出问题深度解析与雪花算法替代方案
mysql·算法
rainbow68898 小时前
深入解析C++STL:map与set底层奥秘
java·数据结构·算法