Skip to content
DSAPrep

    ↑↓ to move · Enter to open · Esc to close

    Kth Largest Element In An Array

    Given an integer array nums and an integer k, return the kth largest element in the array.

    Note that it is the kth largest element in the sorted order, not the kth distinct element.

    Can you solve it without sorting?

    Example 1

    Input: nums = [3,2,1,5,6,4], k = 2

    Output: 5

    Example 2

    Input: nums = [3,2,3,1,2,4,5,5,6], k = 4

    Output: 4

    Constraints

    • 1 <= k <= nums.length <= 10^5
    • -10^4 <= nums[i] <= 10^4
    View original on LeetCode ↗

    The brute-force fix is to sort and index — correct, but it fully orders the array when we only care about one position in it. Two ways to avoid that: keep a min-heap capped at size k (its root ends up being the kth largest), or use Quickselect, which reuses quicksort’s partitioning to zero in on a single target index without sorting either side fully.

    Sort

    Time O(n log n)Space O(log n)

    Sort ascending and read the element k from the end.

    class Solution:
    def findKthLargest(self, nums: list[int], k: int) -> int:
    nums.sort()
    return nums[len(nums) - k]

    Correct and simple, but sorts the entire array to answer a question about a single position.

    Min-Heap of Size k

    OptimalTime O(n log k)Space O(k)

    Keep a min-heap holding only the k largest values seen so far; its root is the smallest of those k, i.e. exactly the kth largest overall. Seed the heap with the first k numbers, then for every remaining number, only bother pushing it if it beats the current root — smaller numbers can never be in the top k and are skipped for free.

    import heapq
    class Solution:
    def findKthLargest(self, nums: list[int], k: int) -> int:
    heap = nums[:k]
    heapq.heapify(heap)
    for n in nums[k:]:
    if n > heap[0]:
    heapq.heapreplace(heap, n) # pop root, push n
    return heap[0]

    Watch the heap root act as a rising bar: every remaining number is measured against it, and only one that beats it earns a swap.

    nums

    321564
    check—waiting…build heap

    min-heap of size k = 2

    —root
    —pos 1
    root = 2nd largest seen so far
    1 / 7
    currentcomparingresultseen

    Start with nums and k = 2. The heap will hold only the 2 largest values seen so far, so it begins empty.

    Correctness: the heap only ever retains the k largest values processed so far; any number smaller than the root is provably outside the top k no matter what comes later (more competition only raises the bar). Once every number is processed, the root is the smallest of the true top k, which is the kth largest.

    Complexity: building the initial heap is O(k), and each of the remaining n-k numbers costs at most O(log k) for a conditional replace → O(n log k) overall. Space is O(k) for the heap.

    Quickselect

    OptimalTime O(n) average, O(n²) worst caseSpace O(1)

    Quicksort partitions around a pivot so everything smaller ends up on one side and everything larger on the other. Quickselect uses the same partition step, but only ever recurses into the one side that contains the target index — the other side is discarded entirely, which is what gets the average case down to linear instead of n log n.

    The kth largest is the element that would land at index n - k in ascending sorted order, so that’s the target index we partition toward. A random pivot matters here — LeetCode includes adversarial test cases that make a fixed pivot (e.g. always picking the last element) degrade to O(n²).

    import random
    class Solution:
    def findKthLargest(self, nums: list[int], k: int) -> int:
    target = len(nums) - k
    def partition(left: int, right: int) -> int:
    pivot_index = random.randint(left, right)
    pivot = nums[pivot_index]
    nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
    store = left
    for i in range(left, right):
    if nums[i] < pivot:
    nums[store], nums[i] = nums[i], nums[store]
    store += 1
    nums[store], nums[right] = nums[right], nums[store]
    return store
    left, right = 0, len(nums) - 1
    while True:
    p = partition(left, right)
    if p == target:
    return nums[p]
    elif p < target:
    left = p + 1
    else:
    right = p - 1

    Correctness: after each partition, the element at index p is in its final sorted position — everything left of p is <= it, everything right is >= it. If p is the target index we’re done; otherwise the target must lie strictly on one side, so it’s safe to discard the other side and recurse only into the half that matters.

    Complexity: on average each partition roughly halves the search space, giving n + n/2 + n/4 + ... ≈ 2n work → O(n) average. With a bad pivot choice every partition could shrink the range by only one element, giving O(n²) worst case — random pivots make this exceedingly unlikely in practice. Partitioning is done in place, so space is O(1) (ignoring recursion — this iterative version uses none).

    More Heap / Priority Queue practice