274. H-Index

Given an array of integers citations where{" "} citations[i] is the number of citations a researcher received for their{" "} ith {" "} paper, return compute the researcher's h -index.

According to the{" "} definition of h-index on Wikipedia : A scientist has an index h if h of their{" "} n papers have at least h citations each, and the other n − h papers have no more than h citations each.

If there are several possible values for h, the maximum one is taken as the h -index.

 

Example 1:

    Input: citations = [3,0,6,1,5]{"\n"}
    Output: 3{"\n"}
    Explanation: [3,0,6,1,5] means the researcher has 5 papers
    in total and each of them had received 3, 0, 6, 1, 5 citations respectively.
    {"\n"}Since the researcher has 3 papers with at least 3 citations each and
    the remaining two with no more than 3 citations each, their h-index is 3.
    {"\n"}
  

Example 2:

    Input: citations = [1,3,1]{"\n"}
    Output: 1{"\n"}
  

 

Constraints:

  • n == citations.length
  • 1 <= n <= 5000
  • 0 <= citations[i] <= 1000

Solution

Naive Solution

We can try all possible values of h from 0to n. For each h, loop through citations to see if h is a possible h-index, using the condition we are given:

A scientist has an index h if h of their n papers have at least h citations each, and the other n-h papers have no more than h citations each.

The answer is the highest h for which this is true.

This takes O(n2)\mathcal{O}(n^2) time because for each of the n+1 possible h values, we have to loop through n citations.

Binary Search Solution

Create a function hasAtLeastHPapersWithHCitations with a parameter h to check if there are at least h papers with >= h citations. When hasAtLeastHPapersWithHCitations(x) is true, hasAtLeastHPapersWithHCitations(x-1) is also true. This means that hasAtLeastHPapersWithHCitations is a monotonic function, so we can binary search for the highest h for which it return true. This h is our h-index.

Time Complexity

Each call to hasAtLeastHPapersWithHCitations checks all nn papers, taking O(n)\mathcal{O}(n).

Binary searching the range [0,n][0, n] takes O(logn)\mathcal{O}(\log n).

Multiplying these together, we take O(nlogn)\mathcal{O}(n \log n).

Space Complexity

citations is passed by reference, so we aren't allocating any memory for it. We allocate a constant amount of memory for a couple of variables, so the space complexity is O(1)\mathcal{O}(1).

C++ Solution

class Solution {
public:
    bool hasAtLeastHPapersWithHCitations(int h, vector<int>& citations) {
        int count = 0;
        for (int cite_count : citations) {
            if (cite_count >= h)
                count++;
        }
        return count >= h;
    }
    int hIndex(vector<int>& citations) {
        int low = 0, high = citations.size();
        while (low <= high) {
            int mid = (low + high) / 2;
            if (hasAtLeastHPapersWithHCitations(mid, citations))
                low = mid + 1;
            else
                high = mid - 1;
        }
        return high;
    }
};

Java Solution

class Solution {
    static boolean hasAtLeastHPapersWithHCitations(int h, int[] citations) {
        int count = 0;
        for (int cite_count : citations) {
            if (cite_count >= h)
                count++;
        }
        return count >= h;
    }
    public int hIndex(int[] citations) {
        int low = 0, high = citations.length;
        while (low <= high) {
            int mid = (low + high) / 2;
            if (hasAtLeastHPapersWithHCitations(mid, citations))
                low = mid + 1;
            else
                high = mid - 1;
        }
        return high;
    }
}

Python Solution

class Solution:
    def hIndex(self, citations: List[int]) -> int:

        def hasAtLeastHPapersWithHCitations(h, citations):
            return sum(cite_count >= h for cite_count in citations) >= h

        low = 0
        high = len(citations)
        while low <= high:
            mid = (low + high) // 2;
            if hasAtLeastHPapersWithHCitations(mid, citations):
                low = mid + 1;
            else:
                high = mid - 1;
        return high

Sort and Loop Solution

First we sort the papers by decreasing # of citations. Imagine a histogram where each bar represents a paper and its height is the # of citations it has.

<img src={require('@site/static/img/274/1.png').default} style={{ height: 450 }}/>

leetcode 274 h-index histogram

If the h-index were h, we'd need exactly h bars with height as least h. That is to say, we'd need the green square covered by bars. To find the h index, first set h = 0. Then keep increasing h by 1 as long as the next tallest bar is >= h+1. When we can no longer increase h, we have our answer.

In the diagram above, if we continued to increase h, the next added bar would not be tall enough for the new h.

Time Complexity

Sorting is O(nlogn)\mathcal{O}(n \log n). Looping h is O(n)\mathcal{O}(n). So the time complexity is O(nlogn)\mathcal{O}(n \log n).

Space Complexity

The only memory we allocate is the integer h, so the space complexity is O(1)\mathcal{O}(1).

C++ Solution

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

Java Solution

class Solution {
    public int hIndex(int[] citations) {
        // Sorting an int[] in reverse in Java is annoying
        // We first sort normally then reverse the array
        Arrays.sort(citations);
        for (int i = 0; i < citations.length/2; i++) {
            int tmp = citations[i];
            citations[i] = citations[citations.length-1-i];
            citations[citations.length-1-i] = tmp;
        }

        int h = 0;
        while (h < citations.length && citations[h] >= h+1) {
            h++;
        }
        return h;
    }
}

Python Solution

class Solution:
    def hIndex(self, citations: List[int]) -> int:
        citations.sort(reverse=True)
        h = 0
        while h < len(citations) and citations[h] >= h+1:
            h += 1
        return h

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator
Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

What's the output of running the following function using input 56?

1KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13    def dfs(path, res):
14        if len(path) == len(digits):
15            res.append(''.join(path))
16            return
17
18        next_number = digits[len(path)]
19        for letter in KEYBOARD[next_number]:
20            path.append(letter)
21            dfs(path, res)
22            path.pop()
23
24    res = []
25    dfs([], res)
26    return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2    '2', "abc".toCharArray(),
3    '3', "def".toCharArray(),
4    '4', "ghi".toCharArray(),
5    '5', "jkl".toCharArray(),
6    '6', "mno".toCharArray(),
7    '7', "pqrs".toCharArray(),
8    '8', "tuv".toCharArray(),
9    '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13    List<String> res = new ArrayList<>();
14    dfs(new StringBuilder(), res, digits.toCharArray());
15    return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19    if (path.length() == digits.length) {
20        res.add(path.toString());
21        return;
22    }
23    char next_digit = digits[path.length()];
24    for (char letter : KEYBOARD.get(next_digit)) {
25        path.append(letter);
26        dfs(path, res, digits);
27        path.deleteCharAt(path.length() - 1);
28    }
29}
30
1const KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13    let res = [];
14    dfs(digits, [], res);
15    return res;
16}
17
18function dfs(digits, path, res) {
19    if (path.length === digits.length) {
20        res.push(path.join(''));
21        return;
22    }
23    let next_number = digits.charAt(path.length);
24    for (let letter of KEYBOARD[next_number]) {
25        path.push(letter);
26        dfs(digits, path, res);
27        path.pop();
28    }
29}
30

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More