【面试经典150 | 数组】H 指数

文章目录

  • 写在前面
  • Tag
  • 题目来源
  • 题目解读
  • 解题思路
    • 方法一:排序
    • 方法二:二分
    • 方法三:计数排序
  • 写在最后

写在前面

本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……

专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:

  • Tag:介绍本题牵涉到的知识点、数据结构;
  • 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
  • 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
  • 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
  • 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。

Tag

【排序】【数组】


题目来源

面试经典150 | 274. H 指数

【面试经典150 | 数组】H 指数_第1张图片

题目解读

有一个数组 citiations,其中 citiations[i] 表示某一个研究员的论文 i 被引用的次数,你要做的是找出该名研究员的 h 指数

h 指数,代表 “高引用次数”,表示有 h 篇论文且每一篇论文被引用了至少 h 次。


解题思路

方法一:排序

我们首先对数组 citiation 进行排序(一般不提及排序方式即升序还是降序排序,默认都是升序排序),然后对排序后的 citiation 从大到小进行遍历。

初始化 h = 0h 表示的是被引用了 h 次的论文数量,我们在遍历的过程中,如果 citiation[i] > h 表明我们找到了 h+1 篇至少被引用了 h+1 次的论文,这时候更新 h++h。我们就这样遍历数组,直到 h 不再增加,最后返回 h 即为最终的答案。

实现代码

class Solution {
public:
    int hIndex(vector<int>& citations) {
        sort(citations.begin(), citations.end());
        int h = 0, i = citations.size() - 1;
        while (i >= 0 && citations[i] > h) {
            ++h;
            --i;
        }
        return h;
    }
};

复杂度分析

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn) n n n 为数组 citiation 的长度,这是排序的时间复杂度。

空间复杂度: O ( l o g n ) O(logn) O(logn),这是排序占用的额外空间。

方法二:二分

我们可以二分枚举答案,即 h 的数量,h 最少有 0 个,最多有 citiations.size() 个。

对于当前二分枚举的答案 mid

  • 如果数组 citiations 中的论文被引用次数大于等于 mid 的论文数大于等于 mid,我们右移二分枚举的下限,因为 h 指数可能更大;
  • 否则,我们左移二分枚举的上限。

计算数组中论文被引用次数大于等于 mid 的论文数直接枚举计数就可以。

实现代码

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int l = 0, r = citations.size();
        while (l < r) {
            int mid = (l + r + 1) >> 1;
            int cnt = 0;
            for (int x : citations) {
                if (x >= mid) ++cnt;
            }

            if (cnt >= mid) l = mid;
            else r = mid - 1;
        }
        return l;
    }
};

复杂度分析

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),二分枚举的时间复杂度为 O ( l o g n ) O(logn) O(logn),每次枚举中需要 O ( n ) O(n) O(n) 的时间复杂度计算数组中论文被引用次数大于等于 mid 的论文数,因此总的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( 1 ) O(1) O(1),使用的额外空间仅仅是几个二分枚举时的辅助变量。

方法三:计数排序

其实还有一种排序的方法,计算排序是对方法一排序时间复杂度的优化。

具体地,我们使用一个数组 cnts 来记录被引用次数从 0n 的对应的论文数量,其中 cnts[n] 表示引用次数大于 n 的论文数量。

首先,遍历数组 citiation 来更新 cnts。最后,我们从后往前遍历数组 cnts,我找到大于等于当前引用次数 i 的论文数量,一旦找到一个 i 直接返回,因为我要找的是最大的 h

实现代码

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();
        vector<int> cnts(n + 1); // 当前引用次数的文章有几篇

        for (int i = 0; i < n; i++) {
            if (citations[i] >= n) {
                cnts[n]++;
            } else {
                cnts[citations[i]]++;
            }
        }

        int cnt = 0;
        for (int i = n; i >= 0; i--) {
            cnt += cnts[i];
            if (cnt >= i) {
                return i;
            }
        }
        return 0;
    }
};

复杂度分析

时间复杂度: O ( n ) O(n) O(n),计数排序的时间复杂度。

空间复杂度: O ( n ) O(n) O(n),使用的额外变量为统计被引用论文数的数组 cnts

写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 哦。

你可能感兴趣的:(面试经典150题,排序,数组,C++,算法)