Builds on Bucket Sort, which you haven’t finished yet.
Counting Sort (H-Index, Maximum Ice Cream Bars)
H-Index (LC 274)(opens leetcode.com)
A researcher has published n papers, and citations[i] is the number of times paper i has been cited. Their h-index is the largest number h for which at least h of the papers each have h or more citations.
Return the researcher's h-index.
h = 3 works. h = 4 would need four papers with 4 or more citations, and only two (6 and 5) have that.Counting Sort
105, heights up to 100, or a count that can never be more than n. You need the values in sorted order, or the smallest, the largest or the k-th of them. A comparison sort takes O(n log n) time, and you want less.- The values spread over a huge range, up to
109in Maximum Gap (LC 164). A list with one slot per value would be too large to build. Each bucket must cover a range of values instead. See Bucket Sort - The question is only "have I seen this value?" or "how many of each value?", with no order, as in Contains Duplicate (LC 217). The values reach
109. A hash map keyed by value holds only the values that occur. See Hash Map - The citations arrive already sorted, and the answer must take
O(log n)time, as in H-Index II (LC 275). Reading every paper to count it would already be too slow. See Lower Bound
| 1 | def h_index(citations: list[int]) -> int: |
| 2 | """H-Index: Counting Sort with every count of n or more kept in slot n. |
| 3 | Time: O(N) | Space: O(N) |
| 4 | """ |
| 5 | n = len(citations) |
| 6 | # count[c]: papers with exactly c citations, for c from 0 to n; count[n] also catches every |
| 7 | # paper with n or more citations, since the h-index can never exceed n. |
| 8 | count = [0] * (n + 1) |
| 9 | Â |
| 10 | # 1. Drop Each Paper Into Its Citation Slot, Capped At n: |
| 11 | # No two papers are compared; capping at n keeps a paper with huge citations inside the list |
| 12 | # and treats it the same as a paper with exactly n citations, which is all h-index can need. |
| 13 | for c in citations: |
| 14 | count[min(c, n)] += 1 |
| 15 | Â |
| 16 | # 2. Walk h Down From n Until Enough Papers Qualify: |
| 17 | # papers always holds the number of papers with at least h citations; each step down adds |
| 18 | # the papers with exactly the new h, so the first h with papers >= h is the largest h that |
| 19 | # works. |
| 20 | h = n |
| 21 | papers = count[n] |
| 22 | while papers < h: |
| 23 | h -= 1 |
| 24 | papers += count[h] |
| 25 | Â |
| 26 | return h |
count[v] is the number of papers with exactly v citations. The last slot, count[n], holds the papers with n or more citations. During the walk, papers is the number of papers with at least h citations.Counting Sort sorts without comparing two values. It works when every value is a whole number in a small range that you know in advance. Make one slot for each possible value. For each value v, add 1 to its slot with count[v] += 1. Then read the slots from the smallest value to the largest. This visits the values in sorted order. In H-Index (LC 274), the loop reads the slots from the top, h = n, down to 0. Each time h goes down by 1, papers adds count[h]. So papers is always the number of papers with at least h citations. The first h where papers >= h is the largest h that works, and that is the answer. This is correct because a slot's position is its value. The slots are already in order, so no comparison is needed. The trap is the size of the list. A paper can have up to 1000 citations, but n can be 1. Then count[c] += 1 writes past the end of a list with n + 1 slots. The h-index can never be larger than n. So a paper with more than n citations counts the same way as a paper with exactly n. Write count[min(c, n)] += 1. The list then needs only n + 1 slots, whatever the citations are.
See it run
Counting Sort, step by step
c -h -papers -
The top row is count, one slot for each number of citations from 0 to n. The bottom row is papers, the number of papers with at least h citations, filled in as the walk goes down.
putthe slot that receives one papercapslot n, which also holds a paper with more than n citationshthe slot read by the walk, while papers < hstopthe slot where papers >= h, which gives the answer
Start: n = 5, so count has 6 slots, numbered 0 to 5. Every slot is 0.
Gotcha 1 of 3
- The trap
Gotcha 1: Indexing
count[c]without the cap atnMistakecount[c] += 1Fixcount[min(c, n)] += 1What happens:
counthas onlyn + 1slots, numbered 0 ton. A paper can have more citations thann. Thencount[c]is past the end of the list, and Python raises an IndexError. The h-index can never be larger thann, so slotncan hold every paper withnor more citations.Example: citations = [100]Mistake: raises IndexErrorFix: returns 1
Deep Dive
What this card adds: one slot for each exact value
Bucket Sort gives each bucket a range of values. Counting Sort is the case where the values are small whole numbers, so a bucket never needs a range. Slot v counts the values that are exactly v.
The procedure has two loops. The first loop does count[v] += 1 for every value. The second loop reads the slots from the smallest value to the largest, or the reverse. The position of a slot is its value, so the slots are already in order and no comparison is needed.
The size of the list is the range of the values plus one. This is why the card needs a small, known range. A range up to 105 is fine. A range up to 109, as in Maximum Gap (LC 164), is too large, and Bucket Sort is the right tool there.
H-Index, traced on five papers
Take citations = [3, 0, 6, 1, 5]. Here n = 5, so count has 6 slots, numbered 0 to 5.
The first loop adds the papers. The paper with 3 citations goes to count[3]. The paper with 0 goes to count[0]. The paper with 6 is marked as the trap on the board. Slot 6 does not exist, so min(6, 5) puts the paper in count[5]. The paper with 1 goes to count[1]. The paper with 5 also goes to count[5], which is now 2.
The walk starts with h = 5 and papers = count[5] = 2. Since 2 < 5, the loop lowers h to 4 and adds count[4] = 0, so papers stays 2. Then h = 3 and papers = 2 + count[3] = 3.
Now papers >= h, because 3 >= 3, so the walk stops. The h-index is 3: three papers have at least 3 citations. The LeetCode example [3, 0, 6, 1, 5] has the answer 3.
Why the cap and the stop test are correct
The h-index can never be larger than n, because it needs h papers and there are only n. So a paper with more than n citations counts the same as a paper with exactly n. The cap count[min(c, n)] += 1 keeps the list at n + 1 slots. Without the cap, citations = [100] raises an IndexError.
While the walk goes down, papers is the number of papers with at least h citations. Each step down to h adds the papers with exactly h citations. Going down makes h smaller and papers larger, so the first h with papers >= h is the largest h that works.
Use while papers < h, not papers != h. For citations = [1, 1, 1], papers goes from 0 to 3 at h = 1, so it is never equal to h. With !=, h keeps falling below 0 and the code raises an IndexError. The correct code stops at h = 1.
The order inside the loop matters too. Lower h first, then add count[h]. If the code adds first, it counts count[n] twice, and [3, 0, 6, 1, 5] returns 4 instead of 3.
Cost, and a second use
The first loop reads n values and the walk visits at most n + 1 slots. The time is O(n) and the space is O(n). A comparison sort costs O(n log n). In general, with m slots, the time is O(n + m) and the space is O(m).
Maximum Ice Cream Bars (LC 1833) uses the same procedure. The costs are at most 105. Count the bars by cost, then walk the prices from 1 upward. At each price, buy min(count[price], coins // price) bars and subtract their cost from coins. Stop when a price cannot be bought in full. For costs = [1, 3, 2, 4, 1] and coins = 7, the answer is 4, and this matches a sort-first version on 20000 random inputs.
60-Second Pitch
"The h-index is at most n, and sorting would cost O(n log n). So I use one slot for each citation count from 0 to n. A paper with more than n citations goes in slot n, because it counts the same as a paper with exactly n. Then I walk h down from n and keep papers, the number of papers with at least h citations. I stop at the first h where papers >= h, and that is the largest h that works. The time is O(n) and the space is O(n). If the values were spread up to 109, I would use ranges instead of exact slots, which is Bucket Sort."
Common Questions
- How is Counting Sort different from Bucket Sort?
Bucket Sort gives each bucket a range of values and needs extra work inside a bucket. Counting Sort gives each exact value its own slot, so a slot holds only a count and nothing needs to be compared. It needs a small range of values.
- Why is the list size n + 1 and not the largest citation count?
The h-index cannot be larger than
n. A paper with more thanncitations counts the same as one with exactlyn, so slotnholds all of them. The list needs onlyn + 1slots, even when a paper has 1000 citations.- Why does the walk go down from n and not up from 0?
Going down,
papersonly grows andhonly shrinks. So the firsthwithpapers >= his the largest one that works, and the walk can stop there. Going up, the test would need the count of papers at or aboveh, which is a suffix sum that the walk does not have yet.- How does H-Index II (LC 275) change the answer?
The citations are already sorted and the answer must take
O(log n)time. Counting reads every paper, which isO(n), so it is too slow. Use binary search on the sorted list.- When is a hash map better than a count list?
When the values reach
109but few of them occur, as in Contains Duplicate (LC 217). A list with one slot for each possible value would be too large. A hash map holds only the values that occur, but it gives no order.
SWE Reasoning
Spot it
- Every value is a whole number in a small range that is known before the data is read: prices up to
105, heights up to 100, or a count that can never be more thann. - The question needs the values in order, or the k-th, smallest or largest of them, as in H-Index (LC 274) and Maximum Ice Cream Bars (LC 1833).
- A comparison sort works but costs
O(n log n), and the limit asks for less.
The invariant, step by step
counthasn + 1slots, all 0.- For each paper with
ccitations,count[min(c, n)] += 1. After the loop,count[v]is the number of papers with exactlyvcitations, andcount[n]holds the papers withnor more. - The walk starts at
h = nwithpapers = count[n]. - While
papers < h, lowerhby 1 and addcount[h]topapers. - At every moment,
papersis the number of papers with at leasthcitations. The firsthwithpapers >= his the answer.
Traps
- No cap at
n.count[c] += 1withc = 100andn = 1raises anIndexError. - The test
papers != h.paperscan jump pasth, andhthen keeps falling below 0. - Adding
count[h]before loweringh. This countscount[n]twice, and[3, 0, 6, 1, 5]returns 4 instead of 3.
Practice
Builds on Bucket Sort, which you haven’t finished yet.
Counting Sort (H-Index, Maximum Ice Cream Bars)
H-Index (LC 274)(opens leetcode.com)
A researcher has published n papers, and citations[i] is the number of times paper i has been cited. Their h-index is the largest number h for which at least h of the papers each have h or more citations.
Return the researcher's h-index.
h = 3 works. h = 4 would need four papers with 4 or more citations, and only two (6 and 5) have that.Counting Sort
105, heights up to 100, or a count that can never be more than n. You need the values in sorted order, or the smallest, the largest or the k-th of them. A comparison sort takes O(n log n) time, and you want less.- The values spread over a huge range, up to
109in Maximum Gap (LC 164). A list with one slot per value would be too large to build. Each bucket must cover a range of values instead. See Bucket Sort - The question is only "have I seen this value?" or "how many of each value?", with no order, as in Contains Duplicate (LC 217). The values reach
109. A hash map keyed by value holds only the values that occur. See Hash Map - The citations arrive already sorted, and the answer must take
O(log n)time, as in H-Index II (LC 275). Reading every paper to count it would already be too slow. See Lower Bound
| 1 | def h_index(citations: list[int]) -> int: |
| 2 | """H-Index: Counting Sort with every count of n or more kept in slot n. |
| 3 | Time: O(N) | Space: O(N) |
| 4 | """ |
| 5 | n = len(citations) |
| 6 | # count[c]: papers with exactly c citations, for c from 0 to n; count[n] also catches every |
| 7 | # paper with n or more citations, since the h-index can never exceed n. |
| 8 | count = [0] * (n + 1) |
| 9 | Â |
| 10 | # 1. Drop Each Paper Into Its Citation Slot, Capped At n: |
| 11 | # No two papers are compared; capping at n keeps a paper with huge citations inside the list |
| 12 | # and treats it the same as a paper with exactly n citations, which is all h-index can need. |
| 13 | for c in citations: |
| 14 | count[min(c, n)] += 1 |
| 15 | Â |
| 16 | # 2. Walk h Down From n Until Enough Papers Qualify: |
| 17 | # papers always holds the number of papers with at least h citations; each step down adds |
| 18 | # the papers with exactly the new h, so the first h with papers >= h is the largest h that |
| 19 | # works. |
| 20 | h = n |
| 21 | papers = count[n] |
| 22 | while papers < h: |
| 23 | h -= 1 |
| 24 | papers += count[h] |
| 25 | Â |
| 26 | return h |
count[v] is the number of papers with exactly v citations. The last slot, count[n], holds the papers with n or more citations. During the walk, papers is the number of papers with at least h citations.Counting Sort sorts without comparing two values. It works when every value is a whole number in a small range that you know in advance. Make one slot for each possible value. For each value v, add 1 to its slot with count[v] += 1. Then read the slots from the smallest value to the largest. This visits the values in sorted order. In H-Index (LC 274), the loop reads the slots from the top, h = n, down to 0. Each time h goes down by 1, papers adds count[h]. So papers is always the number of papers with at least h citations. The first h where papers >= h is the largest h that works, and that is the answer. This is correct because a slot's position is its value. The slots are already in order, so no comparison is needed. The trap is the size of the list. A paper can have up to 1000 citations, but n can be 1. Then count[c] += 1 writes past the end of a list with n + 1 slots. The h-index can never be larger than n. So a paper with more than n citations counts the same way as a paper with exactly n. Write count[min(c, n)] += 1. The list then needs only n + 1 slots, whatever the citations are.
See it run
Counting Sort, step by step
c -h -papers -
The top row is count, one slot for each number of citations from 0 to n. The bottom row is papers, the number of papers with at least h citations, filled in as the walk goes down.
putthe slot that receives one papercapslot n, which also holds a paper with more than n citationshthe slot read by the walk, while papers < hstopthe slot where papers >= h, which gives the answer
Start: n = 5, so count has 6 slots, numbered 0 to 5. Every slot is 0.
Gotcha 1 of 3
- The trap
Gotcha 1: Indexing
count[c]without the cap atnMistakecount[c] += 1Fixcount[min(c, n)] += 1What happens:
counthas onlyn + 1slots, numbered 0 ton. A paper can have more citations thann. Thencount[c]is past the end of the list, and Python raises an IndexError. The h-index can never be larger thann, so slotncan hold every paper withnor more citations.Example: citations = [100]Mistake: raises IndexErrorFix: returns 1
Deep Dive
What this card adds: one slot for each exact value
Bucket Sort gives each bucket a range of values. Counting Sort is the case where the values are small whole numbers, so a bucket never needs a range. Slot v counts the values that are exactly v.
The procedure has two loops. The first loop does count[v] += 1 for every value. The second loop reads the slots from the smallest value to the largest, or the reverse. The position of a slot is its value, so the slots are already in order and no comparison is needed.
The size of the list is the range of the values plus one. This is why the card needs a small, known range. A range up to 105 is fine. A range up to 109, as in Maximum Gap (LC 164), is too large, and Bucket Sort is the right tool there.
H-Index, traced on five papers
Take citations = [3, 0, 6, 1, 5]. Here n = 5, so count has 6 slots, numbered 0 to 5.
The first loop adds the papers. The paper with 3 citations goes to count[3]. The paper with 0 goes to count[0]. The paper with 6 is marked as the trap on the board. Slot 6 does not exist, so min(6, 5) puts the paper in count[5]. The paper with 1 goes to count[1]. The paper with 5 also goes to count[5], which is now 2.
The walk starts with h = 5 and papers = count[5] = 2. Since 2 < 5, the loop lowers h to 4 and adds count[4] = 0, so papers stays 2. Then h = 3 and papers = 2 + count[3] = 3.
Now papers >= h, because 3 >= 3, so the walk stops. The h-index is 3: three papers have at least 3 citations. The LeetCode example [3, 0, 6, 1, 5] has the answer 3.
Why the cap and the stop test are correct
The h-index can never be larger than n, because it needs h papers and there are only n. So a paper with more than n citations counts the same as a paper with exactly n. The cap count[min(c, n)] += 1 keeps the list at n + 1 slots. Without the cap, citations = [100] raises an IndexError.
While the walk goes down, papers is the number of papers with at least h citations. Each step down to h adds the papers with exactly h citations. Going down makes h smaller and papers larger, so the first h with papers >= h is the largest h that works.
Use while papers < h, not papers != h. For citations = [1, 1, 1], papers goes from 0 to 3 at h = 1, so it is never equal to h. With !=, h keeps falling below 0 and the code raises an IndexError. The correct code stops at h = 1.
The order inside the loop matters too. Lower h first, then add count[h]. If the code adds first, it counts count[n] twice, and [3, 0, 6, 1, 5] returns 4 instead of 3.
Cost, and a second use
The first loop reads n values and the walk visits at most n + 1 slots. The time is O(n) and the space is O(n). A comparison sort costs O(n log n). In general, with m slots, the time is O(n + m) and the space is O(m).
Maximum Ice Cream Bars (LC 1833) uses the same procedure. The costs are at most 105. Count the bars by cost, then walk the prices from 1 upward. At each price, buy min(count[price], coins // price) bars and subtract their cost from coins. Stop when a price cannot be bought in full. For costs = [1, 3, 2, 4, 1] and coins = 7, the answer is 4, and this matches a sort-first version on 20000 random inputs.
60-Second Pitch
"The h-index is at most n, and sorting would cost O(n log n). So I use one slot for each citation count from 0 to n. A paper with more than n citations goes in slot n, because it counts the same as a paper with exactly n. Then I walk h down from n and keep papers, the number of papers with at least h citations. I stop at the first h where papers >= h, and that is the largest h that works. The time is O(n) and the space is O(n). If the values were spread up to 109, I would use ranges instead of exact slots, which is Bucket Sort."
Common Questions
- How is Counting Sort different from Bucket Sort?
Bucket Sort gives each bucket a range of values and needs extra work inside a bucket. Counting Sort gives each exact value its own slot, so a slot holds only a count and nothing needs to be compared. It needs a small range of values.
- Why is the list size n + 1 and not the largest citation count?
The h-index cannot be larger than
n. A paper with more thanncitations counts the same as one with exactlyn, so slotnholds all of them. The list needs onlyn + 1slots, even when a paper has 1000 citations.- Why does the walk go down from n and not up from 0?
Going down,
papersonly grows andhonly shrinks. So the firsthwithpapers >= his the largest one that works, and the walk can stop there. Going up, the test would need the count of papers at or aboveh, which is a suffix sum that the walk does not have yet.- How does H-Index II (LC 275) change the answer?
The citations are already sorted and the answer must take
O(log n)time. Counting reads every paper, which isO(n), so it is too slow. Use binary search on the sorted list.- When is a hash map better than a count list?
When the values reach
109but few of them occur, as in Contains Duplicate (LC 217). A list with one slot for each possible value would be too large. A hash map holds only the values that occur, but it gives no order.
SWE Reasoning
Spot it
- Every value is a whole number in a small range that is known before the data is read: prices up to
105, heights up to 100, or a count that can never be more thann. - The question needs the values in order, or the k-th, smallest or largest of them, as in H-Index (LC 274) and Maximum Ice Cream Bars (LC 1833).
- A comparison sort works but costs
O(n log n), and the limit asks for less.
The invariant, step by step
counthasn + 1slots, all 0.- For each paper with
ccitations,count[min(c, n)] += 1. After the loop,count[v]is the number of papers with exactlyvcitations, andcount[n]holds the papers withnor more. - The walk starts at
h = nwithpapers = count[n]. - While
papers < h, lowerhby 1 and addcount[h]topapers. - At every moment,
papersis the number of papers with at leasthcitations. The firsthwithpapers >= his the answer.
Traps
- No cap at
n.count[c] += 1withc = 100andn = 1raises anIndexError. - The test
papers != h.paperscan jump pasth, andhthen keeps falling below 0. - Adding
count[h]before loweringh. This countscount[n]twice, and[3, 0, 6, 1, 5]returns 4 instead of 3.