В дополнение к предлагаемому алгоритму «заглянуть на вершину кучи», который дает вам O (n log m) сложность для получения top-m из n элементов, вот еще два решения.
Решение 1. Используйте кучу Фибоначчи.
Реализация PriorityQueue в JDK представляет собой сбалансированную двоичную кучу. Вы должны иметь возможность повысить производительность из реализации кучи Фибоначчи . Он будет иметь амортизированную вставку с постоянным временем, тогда как вставка в двоичную кучу имеет сложность Ω (log n) в размере кучи. Если вы делаете это для каждого элемента, то вы на Ω (n log n). Поиск топ-m из n элементов с использованием кучи Fib имеет сложность O (n + m log n). Объедините это с предложением вставлять только m элементов в кучу, и у вас будет O (n + m log m), которое настолько близко к линейному времени, сколько вы собираетесь получить.
Решение 2: Пройдите по списку M раз.
Вы должны быть в состоянии получить k-й по величине элемент в наборе за O (n) времени. Просто прочитайте все в список и сделайте следующее:
kthLargest(k, xs)
Pick a random pivot element p from the list
(the first one will do if your list is already random).
Go over the set once and group it into two lists.
Left: smaller than p.
Right: Larger or equal to p.
If the Right list is shorter than k, return kthLargest(k - right.size, Left)
If the Right list is longer than k, return kthLargest(k, right)
Otherwise, return p.
Это дает вам O (n) время. Выполнив это m раз, вы сможете получить объекты top-m в вашем наборе за время O (nm), которое будет строго меньше, чем n log n для достаточно большого n и достаточно малого m. Например, получение топ-10 более миллиона элементов займет вдвое меньше времени, чем при использовании очереди с приоритетами двоичной кучи, при прочих равных условиях.