Solution: Kth Largest Element in a Stream
Let's solve the Kth Largest Element in a Stream problem using the Top K Elements pattern.
Statement
Given an infinite stream of integers (sorted or unsorted), nums
, design a class to find the largest element in a stream.
Note: It is the largest element in the sorted order, not the distinct element.
The class should have the following functions, inputs, and return values:
-
Init(nums, k): It takes an array of integers
nums
and an integerk
and initializes the class object. -
Add(value): It takes one integer
value
, appends it to the stream, and returns the element representing the largest element in the stream.
Constraints:
-
nums.length
-
nums[i]
-
value
- At most, calls will be made to add.
- It is guaranteed that there will be at least elements in the array when you search for the element.
Solution
So far, you have probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.
Naive approach
The naive solution is to first sort the data and then find the largest element. Insertion sort is an algorithm that can be used to sort the data as it appears. However, it also requires shifting the elements, greater than the inserted number, one place forward.
The overall time complexity of the algorithm becomes , where is the number of elements in the data stream. The time complexity of each insertion is and finding the largest element would take time, assuming we are storing the data in an array. The space complexity is .
Optimized approach using Top K Elements
To efficiently find the largest element in a stream of numbers, we use a min-heap that holds the top largest elements. This way, we don’t have to sort the entire list each time a new number is added. The largest element will change as new members come in, so we need a class to handle these dynamic updates.
With its ability to hold k elements, the min-heap ensures that the largest number is always at the root. We do this by adding new elements to the heap and removing the smallest one if the heap grows beyond k elements. This approach allows us quick access to the largest number, making the min-heap the most efficient tool for the job.
The slides below illustrate the core ideas of our algorithm:
Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.
Step-by-step solution construction
We’ll start by defining the constructor. In the constructor, we initialize k and create a min-heap using the elements from the provided array. We then iterate through the elements and add them to the heap by calling the add
function, which will be implemented later on.
import java.util.*;class KthLargest {public PriorityQueue<Integer> topKHeap;public int k;// Constructor to initialize heap and add values in itpublic KthLargest(int k, int[] nums) {System.out.println("\n\tInitializing the heap");this.k = k;topKHeap = new PriorityQueue<>();System.out.println("\tk = " + this.k);System.out.println("\tHeap: " + topKHeap);for (int element : nums) {System.out.println("Adding element " + element + " to the heap.");add(element);}}// Adds element in the heap and return the Kth largestpublic int add(int val) {// Add your implementation herereturn -1; // Placeholder return value}// Driver codepublic static void main(String[] args) {int[] nums = {3, 6, 9, 10};System.out.print("Initial stream: ");printArray(nums);KthLargest kLargest = new KthLargest(3, nums);}public static void printArray(int[] arr) {System.out.print("[");for (int i = 0; i < arr.length; i++) {System.out.print(arr[i]);if (i != arr.length - 1) {System.out.print(", ");}}System.out.print("]");}}
Next, we’ll implement the add
function, which adds an incoming number to the stream. If the heap size is less than , we’ll simply push
the value into the heap. Otherwise, if the incoming value is greater than the smallest element (top of the heap), we’ll perform a pop
operation to remove the smallest element and then push
the new value into the heap. This ensures that the heap maintains only the k largest elements. Since we’re working with a min-heap, the largest element will always be at the top of the heap.
import java.util.*;class KthLargest {public PriorityQueue<Integer> topKHeap;public int k;// Constructor to initialize heap and add values in itpublic KthLargest(int k, int[] nums) {System.out.println("\tInitializing the heap");this.k = k;topKHeap = new PriorityQueue<>();System.out.println("\tk = " + this.k);System.out.println("\tHeap: " + topKHeap);for (int element : nums) {System.out.println("\tAdding element " + element + " to the heap.");add(element);}}// Adds element in the heap and return the Kth largestpublic int add(int val) {if (topKHeap.size() < k) {System.out.println("\tSize of the heap is less than k, so we’ll push " + val);topKHeap.offer(val);} else if (val > topKHeap.peek()) {System.out.println("\tSize of the heap is equal to k and " + val + " is greater than the top of heap " + topKHeap.peek());System.out.println("\tWe will poll the top element from heap and offer the new element.");topKHeap.poll();topKHeap.offer(val);} else {System.out.println("\tSize of the heap is equal to k and " + val + " is equal to or smaller than the top of heap.");System.out.println("\tNo operation will be performed.");}System.out.println("\tHeap: " + topKHeap);return -1; // Placeholder return value}// Driver codepublic static void main(String[] args) {int[] nums = {3, 6, 9, 10};int[] temp = {3, 6, 9, 10};System.out.print("Initial stream: ");printArray(nums);System.out.println("\nk: " + 3);KthLargest kLargest = new KthLargest(3, nums);int[] val = {4, 7, 10, 8, 15};for (int i = 0; i < val.length; i++) {System.out.println("\tAdding a new number " + val[i] + " to the stream");temp = Arrays.copyOf(temp, temp.length + 1);temp[temp.length - 1] = val[i];System.out.print("\tNumber stream: ");printArray(temp);System.out.println("");kLargest.add(val[i]);System.out.println(new String(new char[100]).replace('\0', '-'));}}public static void printArray(int[] arr) {System.out.print("[");for (int i = 0; i < arr.length; i++) {System.out.print(arr[i]);if (i != arr.length - 1) {System.out.print(", ");}}System.out.print("]");}}
We know that the largest element is the top element of the heap. After adding the new element to the heap, we’ll return its top element.
import java.util.*;class KthLargest {public PriorityQueue<Integer> topKHeap;public int k;// Constructor to initialize heap and add values in itpublic KthLargest(int k, int[] nums) {System.out.println("\tInitializing the heap");this.k = k;topKHeap = new PriorityQueue<>();System.out.println("\tk = " + this.k);System.out.println("\tHeap: " + topKHeap);for (int element : nums) {System.out.println("\tAdding element " + element + " to the heap.");add(element);}}// Adds element in the heap and return the Kth largestpublic int add(int val) {if (topKHeap.size() < k) {System.out.println("\n\tSize of the heap is less than k, so we’ll push " + val);topKHeap.offer(val);} else if (val > topKHeap.peek()) {System.out.println("\n\tSize of the heap is equal to k and " + val + " is greater than the top of heap " + topKHeap.peek());System.out.println("\tWe will poll the top element from heap and offer the new element.");topKHeap.poll();topKHeap.offer(val);} else {System.out.println("\n\tSize of the heap is equal to k and " + val + " is equal to or smaller than the top of heap.");System.out.println("\tNo operation will be performed.");}System.out.println("\tHeap: " + topKHeap);return topKHeap.peek();}// Driver codepublic static void main(String[] args) {int[] nums = {3, 6, 9, 10};int[] temp = {3, 6, 9, 10};System.out.print("Initial stream: ");printArray(nums);System.out.println("\nk: " + 3);KthLargest kLargest = new KthLargest(3, nums);int[] val = {4, 7, 10, 8, 15};for (int i = 0; i < val.length; i++) {System.out.println("\tAdding a new number " + val[i] + " to the stream");temp = Arrays.copyOf(temp, temp.length + 1);temp[temp.length - 1] = val[i];System.out.print("\tNumber stream: ");printArray(temp);System.out.println("\tKth largest element in the stream: " + kLargest.add(val[i]));System.out.println(new String(new char[100]).replace('\0', '-'));}}public static void printArray(int[] arr) {System.out.print("[");for (int i = 0; i < arr.length; i++) {System.out.print(arr[i]);if (i != arr.length - 1) {System.out.print(", ");}}System.out.print("]");}}
Just the code
Here’s the complete solution to this problem:
import java.util.*;class KthLargest {public PriorityQueue<Integer> topKHeap;public int k;// Constructor to initialize heap and add values in itpublic KthLargest(int k, int[] nums) {this.k = k;topKHeap = new PriorityQueue<>();for (int element : nums) {add(element);}}// Adds element in the heap and return the Kth largestpublic int add(int val) {if (topKHeap.size() < k) {topKHeap.offer(val);} else if (val > topKHeap.peek()) {topKHeap.poll();topKHeap.offer(val);}return topKHeap.peek();}public static void main(String[] args) {int[] nums = {3, 6, 9, 10};int[] temp = {3, 6, 9, 10};System.out.print("Initial stream: ");printArray(nums);System.out.println("\nk: " + 3);KthLargest kLargest = new KthLargest(3, nums);int[] val = {4, 7, 10, 8, 15};for (int i = 0; i < val.length; i++) {System.out.println("\tAdding a new number " + val[i] + " to the stream");temp = Arrays.copyOf(temp, temp.length + 1);temp[temp.length - 1] = val[i];System.out.print("\tNumber stream: ");printArray(temp);System.out.println("\n\tKth largest element in the stream: " + kLargest.add(val[i]));System.out.println(new String(new char[100]).replace('\0', '-'));}}public static void printArray(int[] arr) {System.out.print("[");for (int i = 0; i < arr.length; i++) {System.out.print(arr[i]);if (i != arr.length - 1) {System.out.print(", ");}}System.out.print("]");}}
Solution summary
To recap, the solution to this problem can be divided into the following parts:
- Initialize a min-heap in the constructor to store the elements. Iterate through the elements in
nums
and call theadd
function for each element. - In the
add
function, if the size of the heap is less than ,push
the number into the heap. Otherwise, if the incoming value is greater than the smallest element (top of the heap), perform apop
operation to remove the smallest element, and thenpush
the new value into the heap. - After adding all the numbers, the largest element can be found at the root of the heap.
Time complexity
-
Init(nums, k): In the constructor, we iterate through the
nums
list with elements and add them to the heap. Adding each element to the heap takes, at most, time. Therefore, the overall time complexity of the constructor is . -
Add(value): In the
add
function, we perform operations like pushing and popping elements from the heap, which take, at most, time. Therefore, the time complexity of the add function is .
Space complexity
The space complexity will be where is the largest element we need to find in a stream. This is because the heap is initialized to have a maximum size of , resulting in an overall space complexity of .
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.