2015-03-11 2 views
4

Я читал некоторые вопросы интервью с практикой, и у меня есть вопрос об этом. Предположим, что список случайных чисел каждый между 1 & 100, вычислить сумму k наибольших целых чисел? Обсудите пространственную и временную сложность и изменится ли подход, если каждое целое число находится между 1 & m, где m изменяется?Эффективный способ вычисления суммы k наибольших чисел в списке?

Моя первая мысль - отсортировать массив и вычислить сумму самых больших k чисел. Затем я подумал, что если я использую двоичную древовидную структуру, где я могу смотреть, начиная с нижнего правого дерева. Я не уверен, изменится ли мой подход, будут ли цифры от 1 до 100 или от 1 до m? Любые мысли о наиболее эффективном подходе?

ответ

4

Наиболее эффективным способом может быть использование чего-то типа randomized quickselect. Он не выполняет этап сортировки до завершения и вместо этого выполняет только шаг раздела с быстрой сортировкой. Если вы не хотите, чтобы k наибольших целых чисел в каком-то определенном порядке, это было бы так, как я бы пошел. Это требует линейного времени, но анализ не очень прост. м не повлияет на это. Кроме того, вы можете написать код таким образом, чтобы сумма вычислялась при разбиении массива.

Time: O(n) 
Space: O(1) 

Альтернативного сортирует используя что-то вроде counting sort который имеет линейную гарантию времени. Как вы говорите, значения являются целыми числами в фиксированном диапазоне, это будет работать очень хорошо. С ростом m увеличивается потребность в пространстве, но вычисление суммы довольно эффективно в пределах ковшей.

Time: O(m) in the worst case (see comments for the argument) 
Space: O(m) 
+0

Если я сделаю это с BST, я бы посмотрел на nlog (n) в среднем? – user1529412

+0

Да, я так думаю. Предполагая, что вы строите сбалансированную двоичную структуру дерева поиска, каждая вставка займет приблизительно O (log n), а после ее создания вам придется суммировать по всему поддереву. Куча была бы более подходящей, но еще более сложной, чем вышеупомянутые решения. –

+0

В альтернативной подсчету не должно быть сложного времени O (n + m)? –

3

Я бы сказал, что сортировка, вероятно, не нужна. Если k невелик, тогда вам нужно только сохранить отсортированный список, который обрезает элементы за пределами элемента k.

Каждый шаг в этом должен быть O(k) в худшем возможном случае, когда элемент добавлен максимально. Тем не менее, средний сценарий случая намного лучше, после определенного количества элементов большинство должно быть просто меньше последнего элемента в списке, а операция будет O(log(k)).

+0

«после определенного количества элементов большинство должно быть меньше последнего элемента в списке» - я не совсем понимаю, как это правда. Даже в среднем случае они являются случайными числами. Кроме того, какую стратегию сортировки вы бы предложили в списке k-length? –

+0

Вам не нужно сортировать. Просто запустите двоичный поиск, чтобы найти местоположение следующего элемента в массиве, а затем переместите все остальное содержимое вправо, отбросив последнее и вставьте новый элемент там, где он принадлежит. После некоторого количества итераций вам не нужно будет переносить большую часть времени, потому что ваш младший элемент будет выше среднего элемента в вашей случайной выборке. –

+1

Я вижу. То, что вы описываете, похоже на [binary insertion sort] (http://en.wikipedia.org/wiki/Insertion_sort#Variants). –

1

Один из способов - использовать min-heap (implemented as a binary tree) максимального размера k. Чтобы увидеть, принадлежит ли новый элемент в куче или нет, это только O (1), так как это мини-куча, а поиск минимального элемента - это операция с постоянным временем. Каждый шаг вставки (или не вставка ... в случае элемента, который слишком мал для вставки) по списку O (n), равен O (log k). Окончательный шаг обхода дерева и суммирования равен O (k).

Общая сложность:

O (n log k + k) = O(n log k))

Если у вас есть несколько ядер, работающих на вашем компьютере, в этом случае, параллельные вычисления является вариант, суммирование должно быть сделано только в конце. «На лету» вычисляет дополнительные этапы расчета, не уменьшая при этом сложную временную сложность (на самом деле у вас будет больше вычислений). Вы всегда должны будете суммировать k элементов, так почему бы не избежать дополнительных шагов сложения и вычитания?

+0

Имеет ли такая сложность использование очереди приоритетов, чтобы сохранить список из K наибольших чисел? – user1529412

+0

@ user1529412 В http://en.wikipedia.org/wiki/Priority_queue говорится: «Хотя очереди приоритетов часто реализуются с помощью куч, они концептуально отличаются от куч. Очередь приоритетов представляет собой абстрактную концепцию типа« список »или« список », карту ", так же, как список может быть реализован со связанным списком или массивом, очередь приоритетов может быть реализована с помощью кучи или множества других методов, таких как неупорядоченный массив." Так что да, он имеет такую ​​же сложность, как * общая реализация * очереди приоритетов. – Shashank

+0

@Shashank Вам нужно использовать кучу минут. Скажем, 1,2,3,4,5 - это числа, и вам нужно найти сумму из 2-х крупнейших чисел. Моя первоначальная куча минут содержит 1,2, когда я читаю 3, я удаляю 1 и вставляю 3, теперь моя куча становится 2, 3. Когда я читаю 4, я удаляю 2 и вставляю 4. Теперь моя куча 3,4. – Sandeep

Смежные вопросы