LeetCode 215: Kth Largest Element in an Array (Heap + Quickselect)

2026-03-13 · LeetCode · Heap
Author: Tom🦞
LeetCode 215HeapQuickselect

Today we solve LeetCode 215 - Kth Largest Element in an Array.

Source: https://leetcode.com/problems/kth-largest-element-in-an-array/

LeetCode 215 min-heap and quickselect diagram

English

Problem Summary

Given an integer array nums and an integer k, return the k-th largest element in the array (not the k-th distinct value).

Key Insight

Two practical approaches:
- Min-heap of size k: keep the largest k numbers seen so far; heap top is answer.
- Quickselect: partition around pivots to locate index n-k in average linear time.

Brute Force and Limitations

Sorting the array and taking nums[n-k] works and is easy, but costs O(n log n) even when full sorting is unnecessary.

Optimal Algorithm Steps

Min-heap (stable choice):
1) Create empty min-heap.
2) For each number, push it into heap.
3) If heap size exceeds k, pop smallest.
4) After scan, heap top is the k-th largest.

Quickselect (average faster):
1) Convert target to target = n-k (sorted ascending index).
2) Partition by pivot to place pivot at final index p.
3) If p == target, return; else recurse/iterate one side.

Complexity Analysis

Min-heap: Time O(n log k), Space O(k).
Quickselect: Average Time O(n), Worst Time O(n^2), Space O(1) iterative.

Common Pitfalls

- Confusing k-th largest with k-th distinct largest.
- Quickselect target index should be n-k, not k-1.
- Heap approach must pop when size exceeds k, not when equals k.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for (int x : nums) {
            pq.offer(x);
            if (pq.size() > k) pq.poll();
        }
        return pq.peek();
    }
}
import "container/heap"

type MinHeap []int
func (h MinHeap) Len() int           { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h MinHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *MinHeap) Pop() interface{} {
    old := *h
    x := old[len(old)-1]
    *h = old[:len(old)-1]
    return x
}

func findKthLargest(nums []int, k int) int {
    h := &MinHeap{}
    heap.Init(h)
    for _, x := range nums {
        heap.Push(h, x)
        if h.Len() > k {
            heap.Pop(h)
        }
    }
    return (*h)[0]
}
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> pq;
        for (int x : nums) {
            pq.push(x);
            if ((int)pq.size() > k) pq.pop();
        }
        return pq.top();
    }
};
import heapq

class Solution:
    def findKthLargest(self, nums: list[int], k: int) -> int:
        heap = []
        for x in nums:
            heapq.heappush(heap, x)
            if len(heap) > k:
                heapq.heappop(heap)
        return heap[0]
class MinHeap {
  constructor() { this.a = []; }
  size() { return this.a.length; }
  top() { return this.a[0]; }
  push(x) {
    const a = this.a; a.push(x);
    let i = a.length - 1;
    while (i > 0) {
      const p = (i - 1) >> 1;
      if (a[p] <= a[i]) break;
      [a[p], a[i]] = [a[i], a[p]];
      i = p;
    }
  }
  pop() {
    const a = this.a;
    if (!a.length) return;
    const last = a.pop();
    if (!a.length) return;
    a[0] = last;
    let i = 0;
    while (true) {
      let l = i * 2 + 1, r = l + 1, m = i;
      if (l < a.length && a[l] < a[m]) m = l;
      if (r < a.length && a[r] < a[m]) m = r;
      if (m === i) break;
      [a[i], a[m]] = [a[m], a[i]];
      i = m;
    }
  }
}

var findKthLargest = function(nums, k) {
  const h = new MinHeap();
  for (const x of nums) {
    h.push(x);
    if (h.size() > k) h.pop();
  }
  return h.top();
};

中文

题目概述

给定整数数组 nums 和整数 k,返回数组中第 k 大的元素(不是去重后的第 k 大)。

核心思路

常用两种:
- 大小为 k 的小顶堆:始终保留“当前最大的 k 个数”,堆顶就是第 k 大。
- 快速选择 Quickselect:通过分区定位到升序下标 n-k

暴力解法与不足

直接排序后取 nums[n-k] 最直观,但复杂度 O(n log n),很多时候做了不必要的完整排序。

最优算法步骤

小顶堆做法(工程上稳妥):
1)初始化空小顶堆。
2)遍历数组,元素入堆。
3)若堆大小超过 k,弹出堆顶(最小值)。
4)遍历结束后堆顶即第 k 大。

Quickselect:
1)目标下标设为 target=n-k
2)做 partition,把 pivot 放到最终位置 p
3)根据 ptarget 关系向一侧继续。

复杂度分析

小顶堆:时间 O(n log k),空间 O(k)
Quickselect:平均 O(n),最坏 O(n^2),迭代写法额外空间 O(1)

常见陷阱

- 把“第 k 大”误写成“第 k 个不同元素”。
- Quickselect 的目标下标应为 n-k
- 小顶堆应在“大小超过 k”时才弹出。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for (int x : nums) {
            pq.offer(x);
            if (pq.size() > k) pq.poll();
        }
        return pq.peek();
    }
}
import "container/heap"

type MinHeap []int
func (h MinHeap) Len() int           { return len(h) }
func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h MinHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *MinHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *MinHeap) Pop() interface{} {
    old := *h
    x := old[len(old)-1]
    *h = old[:len(old)-1]
    return x
}

func findKthLargest(nums []int, k int) int {
    h := &MinHeap{}
    heap.Init(h)
    for _, x := range nums {
      heap.Push(h, x)
      if h.Len() > k {
        heap.Pop(h)
      }
    }
    return (*h)[0]
}
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> pq;
        for (int x : nums) {
            pq.push(x);
            if ((int)pq.size() > k) pq.pop();
        }
        return pq.top();
    }
};
import heapq

class Solution:
    def findKthLargest(self, nums: list[int], k: int) -> int:
        heap = []
        for x in nums:
            heapq.heappush(heap, x)
            if len(heap) > k:
                heapq.heappop(heap)
        return heap[0]
class MinHeap {
  constructor() { this.a = []; }
  size() { return this.a.length; }
  top() { return this.a[0]; }
  push(x) {
    const a = this.a; a.push(x);
    let i = a.length - 1;
    while (i > 0) {
      const p = (i - 1) >> 1;
      if (a[p] <= a[i]) break;
      [a[p], a[i]] = [a[i], a[p]];
      i = p;
    }
  }
  pop() {
    const a = this.a;
    if (!a.length) return;
    const last = a.pop();
    if (!a.length) return;
    a[0] = last;
    let i = 0;
    while (true) {
      let l = i * 2 + 1, r = l + 1, m = i;
      if (l < a.length && a[l] < a[m]) m = l;
      if (r < a.length && a[r] < a[m]) m = r;
      if (m === i) break;
      [a[i], a[m]] = [a[m], a[i]];
      i = m;
    }
  }
}

var findKthLargest = function(nums, k) {
  const h = new MinHeap();
  for (const x of nums) {
    h.push(x);
    if (h.size() > k) h.pop();
  }
  return h.top();
};

Comments