Problem Walkthrough

Kth Largest Element LeetCode 215 Guide

Solve LeetCode 215 Kth Largest Element in an Array using Quickselect for O(n) average time or a min-heap of size k for O(n log k) — learn how randomized pivot selection eliminates the O(n^2) worst case and why each approach shines in different interview and production contexts.

9 min read|

Kth Largest Element

LeetCode 215

Problem Overview — Find the Kth Largest Element

LeetCode 215 asks you to find the kth largest element in an unsorted integer array. Importantly, "kth largest" means the kth largest in sorted order — not the kth distinct element. For example, given nums = [3, 2, 1, 5, 6, 4] and k = 2, the answer is 5 because sorting descending gives [6, 5, 4, 3, 2, 1] and the second element is 5. If nums = [3, 2, 3, 1, 2, 4, 5, 5, 6] and k = 4, the answer is 4 — duplicates count as separate positions in the sorted order.

The naive solution sorts the array descending and returns the element at index k-1, running in O(n log n) time. This always works and is a valid interview answer for a first pass, but the follow-up almost always asks if you can do better. There are two better approaches: a min-heap of size k that runs in O(n log k) time with O(k) space, and Quickselect that achieves O(n) average time with O(1) extra space by partitioning in-place.

The problem is a classic selection problem and appears frequently in FAANG interviews precisely because it has multiple solution tiers — O(n log n) sort, O(n log k) heap, and O(n) average Quickselect — and candidates are expected to discuss the trade-offs between them.

  • Find the kth largest element in sorted order (not kth distinct)
  • Array length 1 to 10^5, values -10^4 to 10^4
  • k is always valid: 1 <= k <= nums.length
  • Naive O(n log n) sort: always correct, simplest implementation
  • O(n log k) heap: safe interview default, predictable performance
  • O(n) average Quickselect: optimal in practice with random pivot

Min-Heap Approach — O(n log k) Time, O(k) Space

The min-heap approach maintains a min-heap of size k as you iterate through the array. For each element, push it onto the heap. If the heap size exceeds k, pop the smallest element. After processing all n elements, the heap contains exactly the k largest elements seen so far, and the top of the heap (the minimum of those k elements) is the kth largest element overall.

This works because the heap invariant ensures that whenever an element is popped, it is smaller than all remaining heap elements. After n elements, the heap holds the k largest values, so its minimum — the heap root — is exactly the kth largest. In Python, heapq is a min-heap by default, so heapq.nlargest(k, nums)[-1] achieves this in one line. In Java, PriorityQueue is a min-heap by default: iterate nums, offer each element, poll if size > k, then peek for the answer.

Time complexity is O(n log k) because each of the n elements may require a heap push and pop, and each heap operation costs O(log k) since the heap never exceeds size k. Space complexity is O(k) for the heap. When k is small relative to n, this is much better than O(n log n) sort. When k ≈ n, the heap degenerates toward O(n log n) and Quickselect becomes preferable.

💡

Min-Heap of Size k Is the Safest Interview Approach

In an interview setting, the min-heap solution is usually the best choice to code first: it is simple to explain (maintain the k largest elements seen so far), reliable with no worst-case edge cases, and gives O(n log k) which is genuinely better than O(n log n) sort. Once you have coded the heap solution correctly, you can discuss Quickselect as an optimization — showing you know both approaches without risking a subtle bug in Quickselect under interview pressure.

Quickselect Algorithm — O(n) Average, O(1) Space

Quickselect is a selection algorithm derived from Quicksort. The key insight is that Quicksort's partition step places one element — the pivot — in its final sorted position and splits the array into elements less than the pivot and elements greater than the pivot. For finding the kth largest element, you only need to recurse into one of the two halves, not both, reducing the average work from O(n log n) to O(n).

To find the kth largest, reframe it as finding the element at index n-k in the sorted order (0-indexed). Choose a pivot, partition the array so all elements greater than the pivot come before it and all smaller elements come after it, and check where the pivot landed. If the pivot is at index n-k, it is the answer. If the pivot is at an index greater than n-k, recurse into the left subarray. If the pivot is at an index less than n-k, recurse into the right subarray. On average, each partition eliminates half the array, giving a geometric series that sums to O(n).

The partition step itself is O(n) — a single pass through the subarray swapping elements relative to the pivot. Unlike Quicksort, Quickselect never recurses into both halves simultaneously, so average total work is O(n) + O(n/2) + O(n/4) + ... = O(2n) = O(n). The space complexity is O(1) extra for an iterative implementation or O(log n) average for a recursive implementation due to the call stack.

  1. 1Reframe: find element at sorted index n-k (0-indexed) where n = len(nums)
  2. 2Choose a pivot element from the current subarray
  3. 3Partition: move elements > pivot to the left, elements < pivot to the right
  4. 4Check the pivot's final index after partitioning
  5. 5If pivot index == n-k: return nums[pivot index] — found the answer
  6. 6If pivot index > n-k: recurse into the left subarray (larger elements)
  7. 7If pivot index < n-k: recurse into the right subarray (smaller elements)

Randomized Pivot — Achieving O(n) Expected Time

The worst case for Quickselect with a fixed pivot (such as always choosing the last element) is O(n^2). This occurs when the array is already sorted or nearly sorted and the pivot consistently lands at one extreme of the partition, reducing the subarray by only one element per step. An array sorted in ascending order with k=1 (find the largest) triggers this worst case if you always pick the last element as pivot.

Randomized pivot selection solves this problem. Before or during each partition step, randomly select a pivot index using a random number generator and swap it with the last element. Because the pivot is uniformly random, the expected partition split is balanced regardless of the input distribution. The expected number of comparisons becomes O(n) regardless of whether the input is sorted, reverse-sorted, or has repeated elements.

In Python, random.randint(left, right) generates a random pivot index within the current subarray bounds. In Java, Random.nextInt(right - left + 1) + left achieves the same. The swap adds O(1) work per partition step and converts the expected time complexity from O(n^2) worst-case to O(n) expected regardless of input, making randomized Quickselect practically superior to the heap approach for large n.

ℹ️

Randomized Quickselect Has O(n) Expected Time With a Tight Constant

With random pivot selection, randomized Quickselect has an expected time of approximately 3.4n comparisons — better than the 2n log k comparisons of a min-heap for large k, and vastly better than 2n log n for the sort approach. The randomness makes the expected runtime independent of input distribution, so even adversarially crafted sorted inputs cannot force the O(n^2) worst case. In practice, randomized Quickselect is the fastest algorithm for this problem across typical inputs.

Median of Medians — Guaranteed O(n) Worst Case

Median of Medians is a deterministic pivot selection strategy that guarantees O(n) worst-case time for Quickselect, eliminating the probabilistic nature of randomized pivot. The algorithm divides the input into groups of 5 elements, finds the median of each group, then recursively finds the median of those medians. This median-of-medians becomes the pivot, guaranteed to be between the 30th and 70th percentile of the array, ensuring the partition eliminates at least 30% of elements each step.

The recurrence for Median of Medians is T(n) ≤ T(n/5) + T(7n/10) + O(n), which resolves to O(n) worst case. The O(n/5) term is the recursive call to find the median of medians on n/5 group medians, T(7n/10) is the recursive Quickselect call on at most 70% of the array, and O(n) is the partition work. This gives O(n) worst case without any randomness.

Despite the theoretical elegance, Median of Medians has a large constant factor — the additional passes to compute group medians add roughly 4-5x the work compared to randomized Quickselect on average inputs. It is rarely used in practice and almost never required in interviews. However, knowing it exists demonstrates thorough understanding of the selection algorithm family and may come up in system design discussions where guaranteed worst-case behavior is required.

  1. 1Divide array into groups of 5 elements each
  2. 2Find the median of each group (sort each 5-element group and take middle)
  3. 3Recursively find the median of the group medians — this is the pivot
  4. 4Partition around this pivot (guaranteed between 30th–70th percentile)
  5. 5Recurse into the appropriate half — at most 70% of array remains
  6. 6Repeat until pivot lands at target index n-k
  7. 7Total work: O(n) worst case, but with a large constant (~5x vs randomized)

Code Walkthrough — Python and Java

Python heap solution: import heapq. def findKthLargest(nums, k): heap = []; for n in nums: heapq.heappush(heap, n); if len(heap) > k: heapq.heappop(heap); return heap[0]. Or more concisely: return heapq.nlargest(k, nums)[-1]. The nlargest shortcut is O(n log k) internally and fine for interviews. Python Quickselect with random pivot: import random. def findKthLargest(nums, k): def partition(left, right): pivot_idx = random.randint(left, right); nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx]; pivot = nums[right]; 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. target = len(nums) - k; left, right = 0, len(nums) - 1; while left < right: idx = partition(left, right); if idx == target: break; elif idx < target: left = idx + 1; else: right = idx - 1; return nums[target].

Java heap solution: public int findKthLargest(int[] nums, int k) { PriorityQueue<Integer> pq = new PriorityQueue<>(); for (int n : nums) { pq.offer(n); if (pq.size() > k) pq.poll(); } return pq.peek(); }. Java Quickselect with random pivot: private Random rand = new Random(); public int findKthLargest(int[] nums, int k) { int target = nums.length - k; int left = 0, right = nums.length - 1; while (left < right) { int idx = partition(nums, left, right); if (idx == target) break; else if (idx < target) left = idx + 1; else right = idx - 1; } return nums[target]; } private int partition(int[] nums, int left, int right) { int pivotIdx = left + rand.nextInt(right - left + 1); swap(nums, pivotIdx, right); int pivot = nums[right], store = left; for (int i = left; i < right; i++) if (nums[i] > pivot) swap(nums, store++, i); swap(nums, store, right); return store; }.

Both implementations use the same Lomuto partition scheme where elements greater than the pivot move to the left (since we want descending order for kth largest). The iterative while loop avoids stack overflow on large inputs. The heap approach is simpler and safer for interviews; Quickselect is faster in practice and worth knowing for follow-up discussion.

⚠️

Quickselect Modifies the Input Array In-Place

Quickselect partitions the array in-place, scrambling the original order of elements. If the caller needs the original array preserved — for example in a function that is called multiple times or when the array is shared with other parts of the program — you must copy the array first: nums = nums[:] in Python or Arrays.copyOf(nums, nums.length) in Java. The heap approach does not modify the input array and is therefore safer when in-place modification is unacceptable.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now