如何将递归算法转换为尾递归算法?

use*_*612 1 c# mergesort tail-recursion

作为进入合并排序的第一次尝试,我生成了以下代码,这些代码适用于字符串,因为它们比列表更容易处理.

class Program
{
    static int iterations = 0;
    static void Main(string[] args)
    {            
        string test = "zvutsrqponmlihgfedcba";
        test = MergeSort(test);
        // test is sorted after 41 iterations
    }

    static string MergeSort(string input)
    {
        iterations++;
        if (input.Length < 2)
            return input;
        int pivot = 0;
        foreach (char c in input)
            pivot += c;
        pivot /= input.Length;
        string left = "";
        string right = "";
        foreach (char c in input)            
            if (c <= (char)pivot)
                left += c;
            else
                right += c;            
        return string.Concat(new string[] { MergeSort(left), MergeSort(right) });
    }
}
Run Code Online (Sandbox Code Playgroud)

在维基百科上阅读可能的优化我发现以下提示"为了确保最多使用O(log N)空间,首先递归到数组的较小一半,并使用尾调用递归到另一个." 但说实话,我不知道如何将这个应用到我的案例中.当我们学习递归和阶乘时,我对我的IT课程的尾部调用有一些模糊的记忆,但我真的无法理解如何将维基百科的建议应用到我的代码中.

任何帮助将非常感激.

Eri*_*ert 8

这个问题有很多问题,首先是你已经实现了一个非常慢的QuickSort版本,但问了一个关于MergeSort的问题.MergeSort通常不实现为尾递归算法.

让我代表你问一个更好的问题:

如何将递归算法转换为尾递归算法?

让我勾勒出一个更简单的尾递归转换,然后你可以找出如何将它应用到你的排序,如果你认为这样做是个好主意.

假设您有以下递归算法:

static int Count(Tree tree)
{
    if (tree.IsEmpty) 
        return 0;
    return 1 + Count(tree.Left) + Count(tree.Right);
}
Run Code Online (Sandbox Code Playgroud)

让我们使用以下有些奇怪的转换将其分解为更多步骤:

static int Count(Tree tree)
{
    int total = 0;
    Tree current = tree;
    if (current.IsEmpty) 
        return 0;
    total += 1;
    int countLeft = Count(current.Left);
    total += countLeft;
    current = current.Right;
    int countRight = Count(current);
    total += countRight;
    return total;
}
Run Code Online (Sandbox Code Playgroud)

请注意,这与以前的程序完全相同,只是更详细.当然你不会以这种冗长的方式编写程序,但它会帮助我们使它呈递尾递归.

尾递归的关键是将递归调用转换为goto.我们可以这样做:

static int Count(Tree tree)
{
    int total = 0;
    Tree current = tree;

 Restart:

    if (current.IsEmpty) 
        return total;
    int countLeft = Count(current.Left);
    total += 1;
    total += countLeft;
    current = current.Right;
    goto Restart;
}
Run Code Online (Sandbox Code Playgroud)

看看我们在那里做了什么?我们不是递归,而是重置当前对已经递归的东西的引用,并返回到开始,同时保持累加器的状态.

现在很清楚如何对QuickSort算法做同样的事情?