C 为什么这种快速排序有效?

C 为什么这种快速排序有效?,c,algorithm,quicksort,C,Algorithm,Quicksort,我发现这种快速排序分区方法既令人困惑又错误,但它似乎是有效的。我指的是注意:他们在文章的最后还有一个C实现,但它与他们的伪代码有很大的不同,所以我不在乎这一点 我也用C写过这样的代码,尽量忠实于伪代码,即使这意味着要做一些奇怪的C代码: #include <stdio.h> int partition(int a[], int p, int r) { int x = a[p]; int i = p - 1; int j = r + 1; whil

我发现这种快速排序分区方法既令人困惑又错误,但它似乎是有效的。我指的是注意:他们在文章的最后还有一个C实现,但它与他们的伪代码有很大的不同,所以我不在乎这一点

我也用C写过这样的代码,尽量忠实于伪代码,即使这意味着要做一些奇怪的C代码:

#include <stdio.h>

int partition(int a[], int p, int r)
{
    int x = a[p];

    int i = p - 1;
    int j = r + 1;

    while (1)
    {
        do
            j = j - 1;
        while (!(a[j] <= x));

        do
             i = i + 1;
        while (!(a[i] >= x));

        if (i < j)
        {
            int t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
        else
        {
            for (i = 1; i <= a[0]; ++i)
                printf("%d ", a[i]);
            printf("- %d\n", j);

            return j;
        }
    }
}


int main()
{
    int a[100] = //{8, 6,10,13,15,8,3,2,12};
                {7, 7, 6, 2, 3, 8, 4, 1};

    partition(a, 1, a[0]);
    return 0;
}
并产生以下输出:

1 6 2 3 8 4 7-1

在我看来,这是一个正确的结果:轴(
a[r]=a[7]
)已到达其最终位置

但是,如果我在以下算法中使用初始分区函数:

void Quicksort(int a[], int p, int r)
{
    if (p < r)
    {
        int q = partition(a, p, r); // initial partitioning function

        Quicksort(a, p, q);
        Quicksort(a, q + 1, r); // I'm pretty sure q + r was a typo, it doesn't work with q + r.
    }
}
void快速排序(int a[],int p,int r)
{
if(p
。。。这似乎是一个正确的排序算法。我在很多随机输入上测试了它,包括所有长度为20的0-1数组

我还尝试将此配分函数用于选择算法,但未能产生正确的结果。它似乎是有效的,但作为快速排序算法的一部分,它甚至非常快

因此,我的问题是:

  • 有人能给出一个算法不起作用的例子吗
  • 如果没有,为什么它会工作,因为分区部分似乎是错误的?这是我不知道的另一种分区方法吗

  • 我认为分区是正确的。7是支点。原始数组被划分为一个长度为5的子数组,其中包含小于或等于7的元素,以及一个长度为2的子数组,其中包含大于或等于7的元素。

    From(我强调了我认为直接解决您问题的部分):

    这是就地分区 算法。它将部分分割开来 左索引和右索引之间的数组的 对,包括移动所有 元素小于或等于 数组[pivotIndex]到 子阵列,留下所有更大的 它们后面的元素。在 处理它也会找到最终结果 枢轴元素的位置,该位置 它回来了它会临时移动 将图元旋转到轴的末端 子阵列,这样它就不会进入 方法。因为它只使用 交换,最后的名单也一样 元素作为原始列表。通知 一个元素可以被交换 在达到其极限之前多次 最后的位置。还应注意到这一点 如果轴在中重复 在输入阵列中,它们可以被分散 穿过左子阵列,可能在 随机顺序。这并不代表一个错误 分区失败,进一步 排序将重新定位并最终 把它们粘在一起


    这可能是您缺少的吗?

    您正在混淆项目的索引和iten值

    看你的头球

    int partition(int a[], int p, int r) ;
    
    现在,如果我们将数组a上的数据类型更改为某种奇怪的数据类型,您将看到问题所在

    int partition( Otherdatatype a[], int p, int r) ;
    
    您可以使用从main中调用函数

    partition(a, 1, a[0]);
    
    请参阅问题a[0]是a[0]中项目的值,而不是索引值

    假设代码中[0]的值为200,只需将第一项的值更改为200,就会出现运行时错误“尝试访问超出范围的内存”,因为 通过将[0]=200作为值r传递到分区中,然后跟踪分区内发生的情况

    需要记住的是,这是分区头中的一个排序例程。数组a中的列表可能与索引的类型不同。。标题的p和r显然是指索引位置的索引,a是要排序的列表

    因此,排序的主要开始是

    partition(a, 0, items_in_array-1);
    
    你明白为什么吗?数组a从[0]运行。。。a[项目\u在\u数组-1中]

    因此,在上面的示例中,您已经将8个值预加载到数组中,因此来自main的分区调用应该是

    partition(a, 0, 7); 
    

    从上面往下延伸就是它的样子

    void swap(int *a, int *b)
    {
        int x;
    
        x = *a;
        *a = *b;
        *b = x;
    }
    
    int partition(int s[], int l, int h) 
    { 
        int i;
        int p;/* pivot element index */ 
        int firsthigh;/* divider position for pivot element */ 
    
        p = h; 
        firsthigh = l; 
        for (i = l; i < h; i++) 
            if(s[i] < s[p]) { 
                swap(&s[i], &s[firsthigh]); 
                firsthigh++; 
            } 
        swap(&s[p], &s[firsthigh]); 
    
        return(firsthigh); 
    }
    
    void quicksort(int s[], int l, int h)
    {
        int p;/* index of partition */ 
        if ((h - l) > 0) {
            p = partition(s, l, h); 
            quicksort(s, l, p - 1); 
            quicksort(s, p + 1, h); 
        } 
    }
    
    int main()     
    {         
        int a[100] = //{8, 6,10,13,15,8,3,2,12};  
                       {7, 7, 6, 2, 3, 8, 4, 1};              
        quicksort(a, 0, 7);
        return 0; 
    }    
    
    无效交换(int*a,int*b)
    {
    int x;
    x=*a;
    *a=*b;
    *b=x;
    }
    整数分区(整数s[],整数l,整数h)
    { 
    int i;
    int p;/*枢轴元素索引*/
    int firsthigh;/*枢轴元件的分隔器位置*/
    p=h;
    第一高=l;
    对于(i=l;i0){
    p=分区(s、l、h);
    快速排序(s、l、p-1);
    快速排序(s、p+1、h);
    } 
    }
    int main()
    {         
    int a[100]=/{8,6,10,13,15,8,3,2,12};
    {7, 7, 6, 2, 3, 8, 4, 1};              
    快速排序(a,0,7);
    返回0;
    }    
    
    你的意思是它是正确的,因为
    a[1..5]
    有元素=
    7
    ?这是真的,但选定的轴不应该到达其最终位置吗?根据我的教科书和维基百科,应该是这样的。在您给出的第一个链接中,quicksort然后对[1..5]和[6..7]进行排序。只有在wikipedia链接中,a[6]的最终值必须是7,因为这里a[1..5]和a[7..7]是在递归调用中排序的。有趣的是,我没有这样想。我一直认为分区算法应该返回pivot元素的最终位置。谢谢你的解释。稍后我会接受你的回答,让其他人也有机会对这个算法发表评论。是的,正如Henrik所说,它是有效的,但重新排序轴心是一种非常浪费的方式。它有一个不同于维基百科的排序不变量([=p]vs[p])。通常情况下,您不会看到以这种方式进行快速排序的原因是它没有那么有效。使用枢轴将产生更多的旋转
    void swap(int *a, int *b)
    {
        int x;
    
        x = *a;
        *a = *b;
        *b = x;
    }
    
    int partition(int s[], int l, int h) 
    { 
        int i;
        int p;/* pivot element index */ 
        int firsthigh;/* divider position for pivot element */ 
    
        p = h; 
        firsthigh = l; 
        for (i = l; i < h; i++) 
            if(s[i] < s[p]) { 
                swap(&s[i], &s[firsthigh]); 
                firsthigh++; 
            } 
        swap(&s[p], &s[firsthigh]); 
    
        return(firsthigh); 
    }
    
    void quicksort(int s[], int l, int h)
    {
        int p;/* index of partition */ 
        if ((h - l) > 0) {
            p = partition(s, l, h); 
            quicksort(s, l, p - 1); 
            quicksort(s, p + 1, h); 
        } 
    }
    
    int main()     
    {         
        int a[100] = //{8, 6,10,13,15,8,3,2,12};  
                       {7, 7, 6, 2, 3, 8, 4, 1};              
        quicksort(a, 0, 7);
        return 0; 
    }