import java.util.List;
import java.util.ArrayList;
/** Collects elements and allows one-time selection of the k largest ones.
*
* Best implemented with a heap!
* The current implementation (using a sorted list) should be replaced
* with something more efficient via heap operations.
*/
public class TopK<T extends Comparable<T>> {
/** The k value that will be used to select the top k. */
private int k;
/** A list to hold the inserted elements. */
private List<T> elements = new ArrayList<>();
/** Create a new, empty instance.
*
* @param k How many items to return from a later call to getTop().
*/
public TopK(int k) {
this.k = k;
}
/** Adds a new element to the collection.
* Note that this method should not be called after getTop() has been called.
*/
public void add(T element) {
if (elements == null)
throw new IllegalStateException("Can't make any other calls after the first call to getTop().");
// NOTE: You may want to change how this works with a heap!
elements.add(element);
}
/** Retrieves the k largest elements that have been added, from largest to smallest.
* Note that this method can only be called once.
* If fewer than k items have been added, then all of them should be returned, sorted
* from largest to smallest.
*/
public List<T> getTop() {
if (elements == null)
throw new IllegalStateException("Can't make any other calls after the first call to getTop().");
// TODO you must change everything below to use a heap instead!
// The current method of repeatedly removing the max is too inefficient.
List<T> largest = new ArrayList<>();
while (!elements.isEmpty() && largest.size() < k) {
int maxInd = 0;
for (int i = 0; i < elements.size(); ++i) {
if (elements.get(i).compareTo(elements.get(maxInd)) > 0)
maxInd = i;
}
largest.add(elements.remove(maxInd));
}
elements = null; // can't call any other methods after calling getTop()
return largest;
}
}