This problem doesn't require sorting the whole given list, which will have a time complexity of O(n log n).
Basically, the task is somewhat similar to finding the maximum element in the list that can be done in O(n) time with a single pass through the given list.
The most suitable approach for this problem is a partial sorting, and the best performance that could be achieved is the middle-ground between O(n) and O(n log n).
In order to find 5
maximum elements in a list, we can maintain a collection that will store in sorted order up to 5
previously encountered elements with maximum values.
Elements that are lover than the smallest element in the collection will be discarded automatically if the collection is already of size 5
. Only new elements with a value higher than the smallest element's value will trigger reordering of this tiny collection. I.e. the data will be sorted partially instead of sorting the whole data set.
In the implementation below for as collection that will store 5
max elements, I've chosen a PriorityQueue
.
According to the documentation it's methods have the following time complexity.
this implementation provides O(log(n)) time for the enqueuing and dequeuing methods (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods; and constant time for the retrieval methods (peek, element, and size).
I.e. adding of a new element and removing the first element both will perform in logarithmic time, and accessing the smallest value in the queue with the method element()
with done in O(1) time (almost immediately).
The PriorityQueue
is encapsulated inside a generic class, which constructor expects as parameters a Comparator<T>
and a desired maximum size of the queue (i.e. a number of max elements that needs to be found).
The queue itself doesn't exposed to the outside classes. Method addItem()
processing the given element and getFirstN
returns a sorted immutable list.
Comparator
in the code below is implemented using the static method comparingInt()
. You could also implement Comparator
in a "classical way" (pre-Java 8) by providing the behavior for it's abstract method compare()
either by using a lambda expression or within an anonymous inner class.
public class FirstN<T> {
private final Queue<T> queue;
private final Comparator<T> comparator;
private final int capacity;
public FirstN(Comparator<T> comparator, int capacity) {
this.queue = new PriorityQueue<>(comparator);
this.comparator = comparator;
this.capacity = capacity;
}
public boolean addItem(T item) {
if (capacity == queue.size() && comparator.compare(item, queue.element()) <= 0) {
return false; // queue is full and the given item is smaller than the lowerest element in the queue
}
if (capacity == queue.size() && comparator.compare(item, queue.element()) > 0) {
queue.remove(); // removing the first element if it's smaller than the given item
}
return queue.add(item); // adding the given item
}
public List<T> getFirstN() {
List<T> result = new ArrayList<>(queue); // creating a list based on a queue
result.sort(comparator);
return List.copyOf(result); // making a copy of the list (returned list is immutable)
}
}
main()
public static void main(String[] args) {
List<City> cities = List.of(
new City("Austin", "Texas", 1028225),
new City("Los Angeles", "California", 3985516),
new City("San Diego", "California", 1429653),
new City("Houston", "Texas", 2325353),
new City("Phoenix", "Arizona", 1759943),
new City("New York City", "New York", 8177025),
new City("San Antonio", "Texas", 1598964),
new City("Philadelphia", "Pennsylvania", 1585480),
new City("San Diego", "California", 1429653),
new City("Chicago", "Illinois", 2671635),
new City("Dallas", "Texas", 1348886));
FirstN<City> top5Cities =
new FirstN<>(Comparator.comparingInt(City::getPopulation), 5);
for (City next: cities) {
top5Cities.addItem(next);
}
List<City> result = top5Cities.getFirstN(); // contains 5 biggest US cities
result.forEach(System.out::println); // printing the result
}
Output (order from lowest to highest)
City [name=Phoenix, state=Arizona, population=1759943]
City [name=Houston, state=Texas, population=2325353]
City [name=Chicago, state=Illinois, population=2671635]
City [name=Los Angeles, state=California, population=3985516]
City [name=New York City, state=New York, population=8177025]