H指数

  • 5 min read
  • Tags: 
  • leetcode

一 问题描述

给定一个整数数组 citations ,其中 citations[i] 代表研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。

h 代表“高引用次数“,一名科研人员的 h 指数是指他(她)至少发表了 h 篇论文,且至少有 h 篇论文的被引用次数大于等于 h 。如果 h 有多种可能值,那 h 指数是其中最大的那个。

274. H 指数


二 解决方法

1 暴力法

分析

因为 h 指数一定小于等于论文数,因此只要穷举所有的论文数,就一定能找到 h 指数。

具体做法如下:使用两层循环,第一层循环尝试所有可能的引用数(最大为论文数)作为 h 指数的候选值,第二层循环遍历数组,记录引用次数大于 h 的元素个数即论文数,找到最大满足「论文数大于引用数」的值,即为 h 值。

代码

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();				// 转储数组大小
        int refCnt = n;							// 初始化最高引用数为数组大小(论文篇数)
        while (refCnt--) {						// 尝试所有可能的引用次数(从高到低)
            int paperCnt = 0;					// 初始化高引用论文数为0
            for (int j = 0; j < n; ++j) {		// 遍历数组
                if (citations[j] >= refCnt) {	// 记录引用次数高于指定次数的论文
                    ++paperCnt;    	
                }
            }
            if (paperCnt >= refCnt) {			// 如果首次找到(即最高)满足条件引用次数,跳出循环
                break;
            }
        }
        return refCnt;							// 返回引用次数
    }
};

复杂度

  • 时间复杂度: O(n^2) ,遍历长度为 n 的数组嵌套遍历两次。

  • 空间复杂度: O(1) ,只使用常数个变量。

2 排序

分析

h 指数代表“高引用次数”,不妨将代表论文引用次数的数组 citations 排序。此时,只要找到一个位置的引用次数,大于其之后的元素个数(论文篇数),该引用次数即为 h 指数。

因为需要计算数组中某一位置元素后的元素个数,反向遍历比较合适,具体做法如下:初始化一变量为0,用于存储元素个数,反向遍历数组,在每次访问完一元素时,累加该变量。

代码

class Solution {
public:
    int hIndex(vector<int>& citations) {
        sort(citations.begin(), citations.end());			// 排序
        int n = citations.size();							// 转储数组打下
        int h = 0;											// 初始化h指数为0
        for (int i = n - 1; i >= 0; --i) {					// 反向遍历排序后的数组
            if (citations[i] > h) {							// 如果引用次数大于h指数
                ++h;										// h指数累加
            }
       } 
       return h;											// 返回h指数
    }
};

复杂度

  • 时间复杂度: O(nlogn) ,即排序长度为 n 数组的时间复杂度。

  • 空间复杂度: O(n) ,即排序长度为 n 数组的空间复杂度。

3 计数排序

计数排序:将原数组以新的计数数组方式存储,以达到排序效果。其中计数数组是:数组下标为元素值,数组元素为原数组元素出现的次数。因为数组下标(此时还表示原数组元素)本就是排序好的,因此也可以看作是一种排序。新的数组大小也就是原数组中最大的值,因此计数排序一般用于整数数组,且最大值已知。

分析

计数排序的关键在于找到原数组的最大值,以方便构建计数数组。由题意知: h 指数不会大于研究者的论文篇数 n ,因此所有引用次数大于 n 的论文都可以存储在第 n+1 个位置。也就是说计数数组大小为 n+1

当获得按计数排序的数组后,反向遍历,累加论文篇数(数组元素),找到首次论文篇数大于等于引用数(数组下标)的下标位置,此时的下标值即为 h 指数。

代码

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();						// 转储数组大小
        int cnt = 0;									// 初始化计数和为0
        vector<int> counter(n + 1, 0);					// 初始化计数数组为{0},用于存储按计数排序后的数组
        for (int i = 0; i < n; ++i) {					// 遍历数组,构建计数数组
            if (citations.at(i) > n) {					// 引用次数大于n,按照次数为n记录
                ++counter[n];							// 
            } else {
                ++counter[citations.at(i)];				// 否则按照实际次数记录
            }
        }
        for (int i = n; i >= 0; --i) {					// 反向遍历计数数组
            cnt += counter.at(i);						// 累加当前引用次数下的论文篇数
            if (cnt >= i) {								// 论文篇数大于等于当前引用次数
                return i;								// 找到h指数,返回结果
            }
        }
        return 0;										// 为找到h指数,返回0
    }
};

复杂度

  • 时间复杂度: O(n) ,遍历数组长度为 ncitations 数组一次,遍历数组长度为 n+1counter 数组一次。

  • 空间复杂度: O(n) ,构建长度为 n+1 的数组 counter


三 总结

  • 当排序能解决问题,且数组元素为整数值时,不妨考虑计数排序。