Существует множество хитроумных подходов (например, решений с приоритетной очередью), но одна из самых простых вещей, которые вы можете сделать, также может быть быстрой и эффективной.
Если вы хотите, чтобы верх k
из n
, рассмотрите:
allocate an array of k ints
while more input
perform insertion sort of next value into the array
Это может показаться абсурдно упрощенным. Вы можете ожидать, что это будет O(n^2)
, но на самом деле это всего лишь O(k*n)
, и если k
намного меньше, чем n
(как это постулируется в постановке задачи), оно приближается к O(n)
.
Вы можете утверждать, что постоянный коэффициент слишком высок, потому что в среднем k/2
сравнений и перемещений на вход много. Но большинство значений будут тривиально отклонены при первом сравнении с k
самым большим значением, которое когда-либо наблюдалось. Если у вас есть миллиард входных данных, скорее всего, только небольшая доля будет больше, чем сотая.
(Вы могли бы построить вход для наихудшего случая, где каждое значение больше, чем его предшественник, что требует k
сравнений и перемещений для каждого входа. Но это по сути отсортированный вход, и проблема В заявлении сказано, что входные данные не отсортированы.)
Даже улучшение бинарного поиска (чтобы найти точку вставки) только сокращает сравнение до ceil(log_2(k))
, и, если вы не сделаете особый случай дополнительного сравнения с k
th-до сих пор, у вас гораздо меньше шансов чтобы получить тривиальный отказ от подавляющего большинства входов. И это не делает ничего, чтобы уменьшить количество ходов, которые вам нужны. Учитывая схемы кэширования и прогноз ветвления, выполнение 7 непоследовательных сравнений, а затем 50 последовательных ходов, похоже, не будет значительно быстрее, чем выполнение 50 последовательных сравнений и ходов. Вот почему многие системные сортировки отказываются от Quicksort в пользу сортировки вставкой для небольших размеров.
Также учтите, что для этого почти не требуется дополнительная память и что алгоритм чрезвычайно дружествен к кешу (что может быть, а может и не быть правдой для кучи или очереди с приоритетами), и писать тривиально без ошибок.
Процесс чтения файла, вероятно, является основным узким местом, поэтому реальный прирост производительности, вероятно, будет достигнут благодаря простому решению для выбора, вы можете сосредоточить свои усилия на поиске хорошей стратегии буферизации для минимизации ввода / вывода .
Если k
может быть сколь угодно большим, приближаясь к n
, то имеет смысл рассмотреть очередь с приоритетами или другую, более разумную структуру данных. Другой вариант - разделить входные данные на несколько частей, отсортировать их параллельно, а затем объединить.