没有尾递归的Quicksort

时间:2016-04-07 11:30:54

标签: c++ recursion quicksort

我使用尾递归编写了以下随机快速排序代码。我想看到不使用尾递归的效果,并希望了解执行时间和运行时间是如何受到影响的。我们如何从下面的随机快速排序代码中删除尾递归?

#include<iostream>
#include<cstdlib>
using namespace std;

int partition(int low,int high,int a[])
{
    int index=(rand()%(high-low))+low;
    //cout<<endl<<index<<"----"<<endl;
    int temp1=a[index];
    a[index]=a[low];
    a[low]=temp1;
    int left,right,pivot_item=a[low];
    left=low;
    right=high;

    while(left<right)
    {
        while(a[left]<=pivot_item)
            left++;
        while(a[right]>pivot_item)
            right--;
        if(left<right)
        {
            int temp=a[right];
            a[right]=a[left];
            a[left]=temp;
        }
    }
    a[low]=a[right];
    a[right]=pivot_item;
    return right;
}

void quicksort(int low,int high,int a[])
{
    int pivot;
    if(low<high)
    {
        pivot=partition(low,high,a);
        quicksort(low,pivot-1,a);
        quicksort(pivot+1,high,a);
    }
}

int main()
{
    srand(time(NULL));
    int n;
    n=50;
    int a[n];
    int i;
    for(i=0;i<n;i++)
        a[i]=(rand()%(1000-1));

    quicksort(0,n-1,a);
    cout<<endl;

    for(i=0;i<n;i++)
        cout<<a[i]<<endl;
    return 0;
}

编辑: 有没有办法完全删除quicksort函数中的第二个递归调用。这意味着删除尾递归并且还会显着影响时间。

2 个答案:

答案 0 :(得分:0)

编译器通常可以选择启用和禁用特定优化。

对于g ++和相关编译器,您可以使用-foptimize-sibling-calls and -fno-optimize-sibling-calls选项启用和禁用尾调用优化。如果您使用其他编译器,请参阅手册。

我建议使用它们来比较尾递归的效果,因为它允许你使用相同的算法。

Quicksort自然是尾递归的,因此制作非尾部复发并不简单。一方面,这很容易:在递归调用之后简单地做一些事情。 Voilà,算法现在不再是尾递归。但是,在递归之后你所做的很重要。它必须具有副作用,否则它可以被优化掉。但是它会影响性能,你不再只测量尾调用优化的效果。

这是一个想法。实现两个相同的函数,如果它们自己在尾部调用中,则相互调用:

void quicksort1(int low,int high,int a[])
{
    int pivot;
    if(low<high)
    {
        pivot=partition(low,high,a);
        quicksort1(low,pivot-1,a);
        quicksort2(pivot+1,high,a);
    }
}

void quicksort2(int low,int high,int a[])
{
    int pivot;
    if(low<high)
    {
        pivot=partition(low,high,a);
        quicksort2(low,pivot-1,a);
        quicksort1(pivot+1,high,a);
    }
}

仍然可以对相互递归的函数进行尾调用优化,所以我不能保证智能编译器不会弄清楚正在发生什么。但优化将更难实现,因此一些编译器可能不会尝试这样做。如果您想确定,请检查装配。此外,这可能会对指令缓存产生一些影响,因此除了禁用尾调用优化之外,它可能会对性能产生影响。

答案 1 :(得分:0)

删除尾递归的一种简单方法是在进行递归调用后在函数中执行其他操作。下面我已经将函数更改为返回值而不是void,并在递归调用完成后添加了一个返回语句来调整某些内容。这应该对性能影响最小。

int quicksort(int low,int high,int a[])
{
    int pivot;
    if(low<high)
    {
        pivot=partition(low,high,a);
        quicksort(low,pivot-1,a);
        quicksort(pivot+1,high,a);
    }
    return 0;
}