Хвост рекурсивной функции позволяет компилятору выполнять специальную оптимизацию, которую он обычно не может при регулярной рекурсии. В хвостовой рекурсивной функции рекурсивный вызов - это самое последнее, что должно быть выполнено. В этом случае, вместо того, чтобы выделять кадр стека для каждого вызова, компилятор может переделать код, чтобы просто повторно использовать текущую кадр стека, что означает, что функция-рекурсивная хвостовая функция будет использовать только одну кадр стека, а не сотни или даже тысячи.
Эта оптимизация возможна, потому что компилятор знает, что после выполнения хвостового рекурсивного вызова, предыдущие копии переменных не потребуются, поскольку для выполнения более нет кода. Если, например, оператор печати следовал за рекурсивным вызовом, компилятор должен был бы знать значение переменной, которая должна быть напечатана после возврата рекурсивного вызова, и, следовательно, кадр стека не может быть использован повторно.
Вот страница вики, если вам нужна дополнительная информация о том, как на самом деле работает эта «экономия пространства» и повторное использование стека, а также примеры: Хвостовый звонок
РЕДАКТИРОВАТЬ: Я не объяснил, как это относится к Quicksort, не так ли? Что ж, в этой статье брошены некоторые термины, которые делают все запутанным (и некоторые из них просто неправильно). Первая выполненная функция (QuickSort) делает рекурсивный вызов слева, рекурсивный вызов справа, а затем выходит. Обратите внимание, что рекурсивный вызов справа - это самое последнее, что происходит в функции. Если компилятор поддерживает хвостовую рекурсивную оптимизацию (объясняется выше), только левые вызовы создают новые кадры стека; Все правильные вызовы просто повторно используют текущую кадр. Это может сохранить немного Кадры стека, но все еще могут страдать от случая, когда разделение создает последовательность вызовов, где оптимизация рекурсии хвоста не имеет значения. Плюс, хотя вызовы правой стороны используют ту же кадр, вызовы левой стороны вызывают в пределах Правые вызовы по-прежнему используют стек. В худшем случае глубина стека - Н.
Вторая описанная версия нет хвостовая рекурсивная QuickSort, а скорее QuickSort, где только левая сортировка выполняется рекурсивно, а правая сортировка выполняется с использованием цикла. Фактически, этот QuickSort (как описано ранее другим пользователем) не может применять оптимизацию рецирсии хвостовой рекурсии, потому что рекурсивный вызов не является последним, чтобы выполнить. Как это работает? При правильном внедрении первый вызов QuickSort такой же, как левый вызов в исходном алгоритме. Тем не менее, рекурсивные вызовы правой стороны даже не называются. Как это работает? Что ж, петля позаботится об этом: вместо сортировки «влево, справа», он сортирует слева с вызовом, а затем сортирует правую, постоянно сортируя только левые. Анкет Это действительно смешно звучит, но в основном это просто сортирует так много левтов, что права становятся единичными элементами и не должны быть отсортированы. Это эффективно удаляет правильную рекурсию, делая функцию менее рекурсивной (псевдо рекурсивным, если хотите). Однако реальная реализация не выбирает только левую сторону каждый раз; он выбирает самую маленькую сторону. Идея все же; В основном он только рекурсивный вызов с одной стороны вместо обеих. Выбор более короткой стороны гарантирует, что глубина стека никогда не может быть больше, чем log2 (n), что является глубиной правильного двоичного дерева. Это связано с тем, что более короткая сторона всегда будет в половине половины размера нашей текущей секции массива. Реализация, данная статьей, не гарантирует это, однако, потому что она может страдать от того же самого наихудшего сценария «левая-это целое дерево». Эта статья на самом деле дает довольно хорошее объяснение этого, если вы хотите больше читать: Эффективный отбор и частичная сортировка на основе QuickSort