Почему QuickSort использует o(log (n)) дополнительное пространство?
я реализовал приведенный ниже алгоритм quicksort. В Интернете я прочитал, что у него есть требование к пространству O(log(n)). Почему это так? Я не создаю никаких дополнительных структур данных.
это потому, что моя рекурсия будет использовать дополнительное пространство на стеке? Если это так, можно ли сделать это с меньшим объемом памяти, не имея рекурсивного (вместо того, чтобы сделать его итеративным)?
private static void quickSort (int[] array, int left, int right) {
int index = partition(array, left, right);
//Sort left half
if (left < index - 1)
quickSort(array, left, index - 1);
//Sort right half
if (index < right)
quickSort(array, index , right);
}
private static int partition (int array[], int left, int right) {
int pivot = array[(left + right) / 2]; //Pick pivot point
while (left <= right) {
//Find element on left that should be on right
while (array[left] < pivot)
left++;
//Find element on right that should be on left
while (array[right] > pivot)
right--;
//Swap elements and move left and right indices
if (left <= right) {
int temp = array[left];
array[left] = array[right];
array[right] = temp;
left++;
right--;
}
}
return left;
}
5 ответов
правильно, дополнительное пространство-это кадры стека журнала(n). От статья Википедии Quicksort:
существует более сложная версия, которая использует раздел на месте алгоритм и может достигнуть полной сортировки, используя o (log n) пространство (не подсчет входных данных) в среднем (по стеку вызовов).
а вы мог бы реализуйте quicksort итеративно (т. е. используя цикл вместо рекурсии), вы затем нужно будет поддерживать вспомогательный стек, потому что Quicksort имеет два рекурсивные вызовы, а не только один.
наконец, как указывали другие ответы, O (log (n)) для почти всех практических приложений очень, очень небольшой. Каждый постоянный фактор, как и накладные расходы вашей структуры данных, будет иметь большее влияние на использование памяти.
чтобы избавиться от рекурсивного вызова, вам придется использовать стек в коде, и он все равно будет занимать log(n)
пространство.
Если Вы читаете дальше в статье Википедии, вы найдете больше подробное обсуждение сложности пространства. В частности, они пишут:
Quicksort с нестабильным разделением на месте использует только постоянное дополнительное пространство перед рекурсивным вызовом. Quicksort должен хранить постоянный объем информации для каждого вложенного рекурсивного вызова. Поскольку в лучшем случае выполняется не более o(log n) вложенных рекурсивных вызовов, он использует o(log n) пространство. Однако, без трюка Седжвика, чтобы ограничить рекурсивные вызовы, в худшем случае quicksort может сделать o(n) вложенные рекурсивные вызовы и нуждаться в o(n) вспомогательном пространстве.
практически говоря, o (log n) память-ничто. Например, если вы сортируете 1 миллиард ints, для их хранения потребуется 4 ГБ, но для стека потребуется только около 30 кадров стека, что-то вроде 40 байтов, то есть около 1200 байтов в общей сложности.
Да, это из-за кадров стека, и да, возможно, можно преобразовать его в итеративный алгоритм, сделав что-то очень умное (хотя ничего не сразу приходит ко мне). Но почему? O(log (n)) пространство-это почти ничто. Для справки, даже если у вас есть массив максимального размера, разрешенного Java, это 2^31 элементов, что составляет около 8 ГБ. Quicksort потребует 31 кадров стека. Стадион, может, 100 байт? Таким образом, 3 КБ всего, что ничто по сравнению с памятью для фактический массив.
на самом деле, почти в любое время, когда что-то O(log(n)), это почти то же самое, что константа.
извините за возрождение этого старого вопроса, но я только что нашел совершенно другой (но немного глупый) ответ на ваш вопрос, на planetmath.org:
любой алгоритм сортировки, который работает на непрерывном массиве, требует O(log n) дополнительное пространство, так как это количество укуса [sic] требуется для представления индекса в массиве.