Problem: Create a class called KthSmallestTracker
. The purpose of the class is to take in new numbers, and to return the kth smallest number that you've seen so far.
Write methods for initializing the class with an integer k
, adding a new number, and returning the kth smallest element you've seen so far.
There will always be at least k elements before getKthSmallest
is called.
Example:
To solve this problem, you need to find the kth smallest element many times.
To do this optimally, you should realize that each time you call getKthSmallest()
, many of the numbers will be the same. For example, here are typical numbers that you might track over time:
Since many of the same numbers appear each time, you should be able to optimize by remembering something about your past computations.
It's not obvious at first (see below for other solutions), but in this problem you can use a Heap data structure to remember past computations. To do this, you can use a Max-Heap that contains the k smallest numbers you've seen so far. This lets you find the kth smallest number quickly - it's the root node of the heap. It also lets you add numbers quickly.
Here's an example maxheap, where k=6:
When we first create the object, we should store the Heap maxheap
, and the size of the heap k
for later. Here's the code for doing this:
We can now write the add
function. When we add a new number, we need to update the maxheap so that it has the k smallest elements in it. Here's the code for this:
Now we've created and updated the heap. We can get the kth smallest element, by looking at the root node of the heap:
Here's the full solution, putting all of the above code together:
Add Function Time Complexity O(logk) for every number added. This is because the heap contains at most k numbers, which means it has a height of O(log k). So adding to it is O(logk).
Find Kth Smallest Element Time Complexity O(1). The kth smallest element is always at the root node of the heap.
Space Complexity O(k) to store the heap.
Note on Sorted Array solution:
You might have thought to use a Sorted Array to solve this problem (store the smallest k items in a Sorted Array), and that way you can quickly look up and add elements. However, there are shift costs to adding a new smallest item in an Array, making the add time O(k). If you try to use a sorted LinkedList, it's still O(k). You might think to turn the sorted array into a BST, and this does give the optimal solution, but it's quite complicated and you need to worry about balancing the BST which is a whole other difficult problem. Using a Heap is much simpler.
TreeNode.of
function can be used to create an entire binary tree using 1 line of code. The input is a BFS traversal of the tree including nulls, and the output is the root node of the tree.TreeNode.of([1, None, 2, None, 3])
ListNode.of
function lets you create an entire Linked List using 1 line of code. The input is an array, and the output is the head of the Linked List.ListNode.of([1, 2, 3])
ListNode.of([1, 2, 3], 1)
GraphNode.of
function lets you create an entire Graph using 1 line of code. The input is an Adjacency Matrix, and the output is the first node in the matrix.GraphNode.of([ ['A', 1, 2], ['B', 2], ['C', 0] ])
[['__init__', 15], ['add', 16], ['get']]
c = MyClass(15) c.add(16) c.get()
TreeNode.of
function can be used to create an entire binary tree using 1 line of code. The input is a BFS traversal of the tree including nulls, and the output is the root node of the tree.TreeNode.of([1, None, 2, None, 3])
ListNode.of
function lets you create an entire Linked List using 1 line of code. The input is an array, and the output is the head of the Linked List.ListNode.of([1, 2, 3])
ListNode.of([1, 2, 3], 1)
GraphNode.of
function lets you create an entire Graph using 1 line of code. The input is an Adjacency Matrix, and the output is the first node in the matrix.GraphNode.of([ ['A', 1, 2], ['B', 2], ['C', 0] ])
[['__init__', 15], ['add', 16], ['get']]
c = MyClass(15) c.add(16) c.get()