Почему 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] требуется для представления индекса в массиве.