Kth Largest Element in an Array – Solution & Complexity

Solution Walkthrough

  1. Step 1: What "kth largest" means

    • Sort nums from largest to smallest and return the value at position k (1-based).
    • For [3,2,1,5,6,4] the descending order is 6, 5, 4, 3, 2, 1, so with k = 2 the answer is 5.
    • The simplest correct solution is exactly that: sort and index. It costs O(n log n) time, but it orders all n values when we only care about the top k.
  2. Step 2: Keep only the k largest values seen so far

    • Scan the array once and keep a small set of candidates: the k largest values seen so far.
    • When a new value arrives, it joins the candidates. If that makes k + 1 candidates, the smallest one can never be the answer any more — at least k values are already larger than or equal to it — so drop it.
    • After the last value, the candidates are exactly the k largest values in the array, and the smallest candidate is the kth largest.
    • So we need a structure that cheaply answers "what is the smallest candidate?" and removes it. That is a min-heap of size at most k.
  3. Step 3: Why a min-heap (not a max-heap)

    • Invariant: after processing each value, the heap holds the k largest values seen so far (or all of them, if fewer than k have been seen).
    • A min-heap keeps its smallest element at the root, so the candidate to evict is always heap[0]. Push and pop each cost O(log k) because the heap never grows past k + 1 elements.
    • At the end the root is the smallest of the k largest values, i.e. the kth largest. Read it without popping.
    • A max-heap would put the largest value on top, which is the wrong value to evict. You could instead heapify all n values into a max-heap and pop k - 1 times, but that keeps O(n) values in memory instead of O(k).
  4. Step 4: Trace: nums = [3,2,1,5,6,4], k = 2

    Each step pushes the value, then pops the root if the heap holds more than k = 2 values. The candidates column lists the retained values sorted for readability; it is not the heap's internal array layout.

    ValueAfter push (sorted)Popped rootCandidates after step (sorted)Root
    3{3}—{3}3
    2{2, 3}—{2, 3}2
    1{1, 2, 3}1{2, 3}2
    5{2, 3, 5}2{3, 5}3
    6{3, 5, 6}3{5, 6}5
    4{4, 5, 6}4{5, 6}5

    The final candidates {5, 6} are the two largest values, and the root 5 is the answer.

  5. Step 5: Duplicates count toward the rank

    • Equal values occupy separate positions. For nums = [3,2,3,1,2,4,5,5,6] the descending order is 6, 5, 5, 4, 3, 3, 2, 2, 1.
    • With k = 4 the answer is 4. If you deduplicated first, the distinct values 6, 5, 4, 3 would give 3 — a different (wrong) answer.
    • The heap handles this naturally: both 5s are pushed as separate entries, and the final candidates are {4, 5, 5, 6} with root 4. Do not use a set for the candidates.
    • Other edge cases: k = 1 returns the maximum, k = n returns the minimum, negative values and all-equal arrays (e.g. [5,5,5], k = 2 → 5) need no special handling.
  6. Step 6: Final Solution and Complexity

    Every language below uses a size-k min-heap: push each value, pop the root when the size exceeds k, and return the root at the end. Python uses heapq, Java PriorityQueue, C++ priority_queue with std::greater, Rust BinaryHeap<Reverse<i32>>, and JavaScript, Go and Ruby a small hand-written binary min-heap.

    ApproachTimeExtra space
    Sort, then indexO(n log n)O(n) or O(1) depending on the sort
    Size-k min-heap (this solution)O(n log k)O(k)
    Quickselectexpected O(n), worst case O(n²)O(1) with an in-place partition
    • The heap is a good default: predictable O(n log k) time, works on a stream, and only O(k) memory.
    • Quickselect partitions around a pivot and recurses into one side only. With random pivots it runs in expected linear time, but consistently bad pivots degrade it to O(n²).
    def find_kth_largest(nums: list[int], k: int) -> int:
        import heapq
        heap = []
        for number in nums:
            heapq.heappush(heap, number)
            if len(heap) > k: heapq.heappop(heap)
        return heap[0]

FAQ