Uma chamada de função recursiva final permite que o compilador execute uma otimização especial que normalmente não consegue com a recursão regular.Em uma função recursiva final, a chamada recursiva é a última coisa a ser executada.Nesse caso, em vez de alocar um quadro de pilha para cada chamada, o compilador pode retrabalhar o código para simplesmente reutilizar o quadro de pilha atual, o que significa que uma função recursiva final usará apenas um único quadro de pilha em vez de centenas ou mesmo milhares.
Essa otimização é possível porque o compilador sabe que uma vez feita a chamada recursiva final, nenhuma cópia anterior das variáveis será necessária, pois não há mais código para executar.Se, por exemplo, uma instrução print seguisse uma chamada recursiva, o compilador precisaria saber o valor da variável a ser impressa após o retorno da chamada recursiva e, portanto, o quadro da pilha não poderia ser reutilizado.
Aqui está a página wiki se você quiser mais informações sobre como essa "economia de espaço" e reutilização de pilha realmente funciona, junto com exemplos: Chamada final
Editar:Não expliquei como isso se aplica ao quicksort, não é?Bem, alguns termos são usados nesse artigo que tornam tudo confuso (e alguns deles são simplesmente errados).A primeira função fornecida (QUICKSORT) faz uma chamada recursiva à esquerda, uma chamada recursiva à direita e depois sai.Observe que a chamada recursiva à direita é a última coisa que acontece na função.Se o compilador suportar otimização recursiva final (explicada acima), apenas as chamadas à esquerda criarão novos quadros de pilha;todas as chamadas certas apenas reutilizam o quadro atual.Isso pode salvar alguns stack frames, mas ainda pode sofrer no caso em que o particionamento cria uma sequência de chamadas onde a otimização da recursão final não importa.Além disso, embora as chamadas do lado direito usem o mesmo quadro, as chamadas do lado esquerdo chamadas dentro de as chamadas do lado direito ainda usam a pilha.Na pior das hipóteses, a profundidade da pilha é N.
A segunda versão descrita é não um quicksort recursivo de cauda, mas sim um quicksort onde apenas a classificação à esquerda é feita recursivamente e a classificação à direita é feita usando o loop.Na verdade, esse quicksort (conforme descrito anteriormente por outro usuário) não pode ter a otimização de recursão final aplicada a ele, porque a chamada recursiva não é a última coisa a ser executada.Como é que isso funciona?Quando implementada corretamente, a primeira chamada para quicksort é igual à chamada do lado esquerdo no algoritmo original.No entanto, nenhuma chamada recursiva do lado direito é chamada.Como é que isso funciona?Bem, o loop cuida disso:em vez de classificar "esquerda e direita", ele classifica a esquerda com uma chamada e, em seguida, classifica a direita classificando continuamente apenas os esquerdas da direita.Parece realmente ridículo, mas basicamente é apenas classificar tantas esquerdas que as direitas se tornam elementos únicos e não precisam ser classificadas.Isso efetivamente remove a recursão correta, tornando a função menos recursiva (pseudo recursiva, se preferir).Contudo, a implementação real não escolhe apenas o lado esquerdo de cada vez;ele escolhe o menor lado.A ideia ainda é a mesma;basicamente faz apenas uma chamada recursiva de um lado em vez de ambos.Escolher o lado mais curto garantirá que a profundidade da pilha nunca possa ser maior que log2(N), que é a profundidade de uma árvore binária adequada.Isso ocorre porque o lado mais curto sempre terá no máximo metade do tamanho da seção atual do array.A implementação dada pelo artigo não garante isso, pois pode sofrer do mesmo pior cenário de "a esquerda é a árvore inteira".Na verdade, este artigo fornece uma boa explicação sobre isso, se você estiver disposto a ler mais: Seleção eficiente e classificação parcial baseada em quicksort