← Back to DevBytes

Solving Kth Largest Element in Array in JavaScript: Step-by-Step Guide

Solving Kth Largest Element in Array in JavaScript: Step-by-Step Guide

Finding the Kth largest element in an array is one of the most frequently asked algorithmic problems in coding interviews and a fundamental operation in many real-world applications. Whether you are building a leaderboard for a game, filtering top-performing stocks, or implementing a priority queue, understanding how to efficiently retrieve the Kth largest value is essential. In this guide, we will explore multiple approaches to solve this problem in JavaScript, analyze their time complexities, and discuss best practices.

What Is the Kth Largest Element Problem?

Given an array of numbers and an integer k, the task is to find the element that would be in the Kth position if the array were sorted in descending order. For example, in the array [3, 2, 1, 5, 6, 4] with k = 2, the sorted descending array would be [6, 5, 4, 3, 2, 1], so the 2nd largest element is 5.

It is important to note that "Kth largest" refers to the Kth distinct position in sorted order, not the Kth distinct value. If the array contains duplicates, they each occupy their own position. For instance, in [3, 3, 3, 2, 1] with k = 2, the answer is 3 because the sorted descending array is [3, 3, 3, 2, 1].

Why It Matters

This problem is more than just an interview exercise. It appears in many practical scenarios:

The key challenge is efficiency. A naive solution may work for small arrays, but as the input grows, the choice of algorithm dramatically affects performance. Understanding the trade-offs between different approaches is what separates a working solution from an optimal one.

Approach 1: Sorting

The simplest approach is to sort the array in descending order and return the element at index k - 1. This is easy to implement and works well for small to medium-sized arrays.

function findKthLargestSort(nums, k) {
  // Sort in descending order
  const sorted = nums.slice().sort((a, b) => b - a);
  return sorted[k - 1];
}

// Example usage
const arr = [3, 2, 1, 5, 6, 4];
console.log(findKthLargestSort(arr, 2)); // Output: 5

Time Complexity: O(n log n) due to sorting.

Space Complexity: O(n) because we create a copy of the array with slice(). If you sort in place, it becomes O(log n) due to the sorting algorithm's stack space.

While this approach is straightforward, it does more work than necessary. Sorting the entire array when we only need one element is wasteful, especially for large arrays.

Approach 2: Using a Min-Heap

A more efficient approach for large arrays uses a min-heap of size k. The idea is to maintain a heap containing the K largest elements seen so far. The smallest element in this heap is the Kth largest overall.

JavaScript does not have a built-in heap data structure, so we need to implement one. Here is a simple min-heap implementation:

class MinHeap {
  constructor() {
    this.heap = [];
  }

  size() {
    return this.heap.length;
  }

  peek() {
    return this.heap[0];
  }

  insert(val) {
    this.heap.push(val);
    this.bubbleUp(this.heap.length - 1);
  }

  extractMin() {
    const min = this.heap[0];
    const last = this.heap.pop();
    if (this.heap.length > 0) {
      this.heap[0] = last;
      this.bubbleDown(0);
    }
    return min;
  }

  bubbleUp(index) {
    while (index > 0) {
      const parentIndex = Math.floor((index - 1) / 2);
      if (this.heap[parentIndex] <= this.heap[index]) break;
      [this.heap[parentIndex], this.heap[index]] =
        [this.heap[index], this.heap[parentIndex]];
      index = parentIndex;
    }
  }

  bubbleDown(index) {
    const length = this.heap.length;
    while (true) {
      let leftChild = 2 * index + 1;
      let rightChild = 2 * index + 2;
      let smallest = index;

      if (leftChild < length && this.heap[leftChild] < this.heap[smallest]) {
        smallest = leftChild;
      }
      if (rightChild < length && this.heap[rightChild] < this.heap[smallest]) {
        smallest = rightChild;
      }
      if (smallest === index) break;

      [this.heap[index], this.heap[smallest]] =
        [this.heap[smallest], this.heap[index]];
      index = smallest;
    }
  }
}

function findKthLargestHeap(nums, k) {
  const heap = new MinHeap();
  for (const num of nums) {
    heap.insert(num);
    if (heap.size() > k) {
      heap.extractMin();
    }
  }
  return heap.peek();
}

// Example usage
const arr2 = [3, 2, 1, 5, 6, 4];
console.log(findKthLargestHeap(arr2, 2)); // Output: 5

Time Complexity: O(n log k) because we perform heap operations for each of the n elements, and each operation on a heap of size k takes O(log k) time.

Space Complexity: O(k) for the heap.

This approach is particularly useful when k is much smaller than n, or when data arrives in a stream and you cannot store the entire array in memory.

Approach 3: Quickselect Algorithm

Quickselect is an algorithm based on the partitioning step of quicksort. It has an average time complexity of O(n), making it the most efficient approach for this problem on average. The idea is to choose a pivot, partition the array around it, and recursively search only the side that contains the Kth largest element.

function findKthLargestQuickSelect(nums, k) {
  // We are looking for the element at index (nums.length - k)
  // in a sorted ascending array
  const targetIndex = nums.length - k;

  function partition(left, right, pivotIndex) {
    const pivotValue = nums[pivotIndex];
    // Move pivot to the end
    [nums[pivotIndex], nums[right]] = [nums[right], nums[pivotIndex]];
    let storeIndex = left;

    for (let i = left; i < right; i++) {
      if (nums[i] < pivotValue) {
        [nums[storeIndex], nums[i]] = [nums[i], nums[storeIndex]];
        storeIndex++;
      }
    }
    // Move pivot to its final place
    [nums[right], nums[storeIndex]] = [nums[storeIndex], nums[right]];
    return storeIndex;
  }

  function quickSelect(left, right) {
    if (left === right) return nums[left];

    // Choose a random pivot index
    const pivotIndex = Math.floor(Math.random() * (right - left + 1)) + left;
    const newPivotIndex = partition(left, right, pivotIndex);

    if (newPivotIndex === targetIndex) {
      return nums[newPivotIndex];
    } else if (newPivotIndex < targetIndex) {
      return quickSelect(newPivotIndex + 1, right);
    } else {
      return quickSelect(left, newPivotIndex - 1);
    }
  }

  return quickSelect(0, nums.length - 1);
}

// Example usage
const arr3 = [3, 2, 1, 5, 6, 4];
console.log(findKthLargestQuickSelect(arr3, 2)); // Output: 5

Time Complexity: O(n) on average, but O(n²) in the worst case if the pivot selection is consistently poor. Using a random pivot significantly reduces the probability of hitting the worst case.

Space Complexity: O(log n) on average due to recursion stack, O(n) in the worst case.

Quickselect is the go-to algorithm when you need optimal average-case performance and can afford to modify the input array in place.

Comparing the Approaches

Best Practices

When implementing a solution to this problem, keep the following best practices in mind:

Here is an example that includes input validation:

function findKthLargest(nums, k) {
  if (!Array.isArray(nums) || nums.length === 0) {
    throw new Error("Input must be a non-empty array");
  }
  if (k < 1 || k > nums.length) {
    throw new Error("k must be between 1 and the array length");
  }

  // Use quickselect for optimal average performance
  return findKthLargestQuickSelect(nums.slice(), k);
}

Conclusion

Finding the Kth largest element in an array is a classic problem that tests your understanding of sorting, heaps, and partitioning algorithms. While the sorting approach offers simplicity, the min-heap and quickselect algorithms provide significant performance improvements for larger datasets. By understanding the trade-offs between these approaches and applying best practices such as input validation and careful pivot selection, you can write efficient, robust solutions tailored to your specific use case. Whether you are preparing for an interview or building a production application, mastering these techniques will make you a more effective JavaScript developer.

— Ad —

Google AdSense will appear here after approval

← Back to all articles