【问题标题】:implementation of quick sort, almost working but not快速排序的实现,几乎可以工作但不是
【发布时间】:2017-06-06 19:34:50
【问题描述】:

尝试用这段代码here实现快速排序算法

代码几乎可以工作,除了数组中的一个元素没有被排序,我认为它必须与partition函数中使用的i,j做一些事情,不确定..

我的实施是否正确?

这些是相关的功能:

int partition(int a[], int start, int end){
  int i,j,pivot,temp;

  pivot = a[end]; //select last elem as the pivot
  i = start;   // point i and j to before/after start end of the arr
  j= end-1;

  while(true){
    //reduce j until we reach an elem that is less than pivot
    if(a[j] >= pivot){ j= j-1; }    
    //increase i until we reach an elem that is more than pivot
    if(a[i] <= pivot){ i = i+1; }  


    if(i<j){  //swap elems so that it is partitioned
          temp = a[j];
          a[j] = a[i];
          a[i] = temp;
    }
    else{ return j; } //return the boundary for this partition
  }
}

void quick_sort(int arr[], int start, int end){
   int boundary;
   if(start<end){
         boundary = partition(arr,start,end);

         quick_sort(arr,start,boundary);
         quick_sort(arr,boundary+1, end);
   }
   return;
}

初始调用是:quick_sort(a,0,n); 其中a 是一个长度为n 的数组

这是我要遵循的算法:(pr 是开始和结束索引)

Partition(A,p,r)
    x <- A[p]
    i <- p-1
    j <- r+1
    while (True) {
        repeat
            j <- j-1
        until (A[j] <= x)
        repeat
            i <- i+1
        until (A[i] >= x)
        if (i <j) 
           exchange A[i] <- A[j]
        else 
            return(j)
    }
}

查看了我可能出错的所有可能情况,无法将问题归零。在此先感谢您的帮助。

【问题讨论】:

  • C++ 中的数组通常从 0 而非 1 开始索引 — 因此,您使用 quick_sort(a, 1, n) 是一个因素吗?
  • 通常在类 C 语言中,长度为 n 的数组从元素 0 开始并以元素 n - 1...
  • 数组长度 n 的起始 j 值是什么?为什么要使用奇怪的移位参数值?
  • 您拥有if (i A[j] else return(j) 的伪代码部分缺少关键信息。您的分区算法不代表伪代码中的两个嵌套 repeat … until 循环。

标签: c++ algorithm sorting quicksort


【解决方案1】:

通过 1

在您的(原始)分区代码中,您有:

int partition(int a[], int start, int end){
  int i,j,pivot,temp;

  pivot = a[end]; //select last elem as the pivot
  i = start- 1;   // point i and j to before/after start end of the arr
  j= end +1;

  while(true){
    //reduce j until we reach an elem that is less than pivot
    if(a[j] >= pivot){ j= j-1; }    
    //increase i until we reach an elem that is more than pivot
    if(a[i] <= pivot){ i = i+1; }  

您应该只访问a[start]a[end],但您从访问这些边界之外的循环的第一次迭代开始。这肯定是问题的一部分。

修复不一定那么明显。放弃减法和加法是很诱人的,但这并不一定是正确的。关键的事情之一是获得足够有用的诊断打印以了解正在发生的事情。

通过 2

自第 1 阶段以来,已添加并修复了伪代码。伪代码与 Wikipedia 上的 QuickSort 非常相似。这是“Hoare Partitioning”主题的一个小变体——变体是在问题中使用repeat … until,而在维基百科中使用do … while

algorithm quicksort(A, lo, hi) is
    if lo < hi then
        p := partition(A, lo, hi)
        quicksort(A, lo, p)
        quicksort(A, p + 1, hi)

algorithm partition(A, lo, hi) is
    pivot := A[lo]
    i := lo - 1
    j := hi + 1
    loop forever
        do
            i := i + 1
        while A[i] < pivot

        do
            j := j - 1
        while A[j] > pivot

        if i >= j then
            return j

        swap A[i] with A[j]

你当前版本的分区码是:

int partition(int a[], int start, int end){
  int i,j,pivot,temp;

  pivot = a[end]; //select last elem as the pivot
  i = start;   // point i and j to before/after start end of the arr
  j= end-1;

  while(true){
    //reduce j until we reach an elem that is less than pivot
    if(a[j] >= pivot){ j= j-1; }    
    //increase i until we reach an elem that is more than pivot
    if(a[i] <= pivot){ i = i+1; }  

    if(i<j){  //swap elems so that it is partitioned
          temp = a[j];
          a[j] = a[i];
          a[i] = temp;
    }
    else{ return j; } //return the boundary for this partition
  }
}

有许多关键区别:

  1. 您正在使用最后一个元素而不是第一个元素作为枢轴。
  2. 在递增 i 和递减 j 时,您根本没有循环。

解决这些问题会产生如下代码:

static inline void swap_ints(int *A, int i, int j)
{
    int t = A[i];
    A[i] = A[j];
    A[j] = t;
}

static int partition(int a[], int start, int end)
{
    int pivot = a[start];   // select first elem as the pivot
    //printf("-->> P(%d,%d) - pivot %d\n", start, end, pivot);
    int i = start - 1;      // point i before start of the array
    int j = end + 1;        // point j after end of the array

    while (true)
    {
        do
        {
            j--;
            //printf("---- j a[%d] = %d\n", j, a[j]);
        } while (a[j] > pivot);
        do
        {
            i++;
            //printf("---- i a[%d] = %d\n", i, a[i]);
        } while (a[i] < pivot);
        if (i >= j)
            break;
        //printf("-<>- a[%d]=%d, a[%d]=%d\n", j, a[j], i, a[i]);
        swap_ints(a, i, j);
        //printf("-><- a[%d]=%d, a[%d]=%d\n", j, a[j], i, a[i]);
        //dump_data("Post-swap", a, start, end);
    }
    //dump_data("Partition", a, start, end);
    //printf("<<-- P(%d) = %d\n", j, a[j]);
    return j;
}

有很多被注释掉的打印功能,有时用来让我确信事情正在按预期工作。

您的主要快速排序功能还可以,尽管它也被(大量)印刷装饰:

void quicksort(int arr[], int start, int end)
{
    if (start < end)
    {
        dump_data("QS Pre-partition", arr, start, end);
        int boundary = partition(arr, start, end);
        printf("QS Partition: %d:%d:%d\n", start, boundary, end);
        dump_data("QS Pre-recursion L", arr, start, boundary);
        quicksort(arr, start, boundary);
        dump_data("QS Pre-recursion H", arr, boundary + 1, end);
        quicksort(arr, boundary + 1, end);
        dump_data("QS Post-Sort", arr, start, end);
    }
}

dump_data() 函数如下所示:

void dump_data(const char *tag, int *data, int start, int end)
{
    printf("%s (%d):", tag, end - start + 1);
    for (int i = start; i <= end; i++)
    {
        printf(" %d", data[i]);
    }
    putchar('\n');
}

测试代码(main())看起来像:

int main(void)
{
    int data[] =
    {
        /* random -n 20 0 9 | commalist -b '        ' -n 10 */
        //4, 8, 0, 0, 3, 8, 3, 6, 5, 9,
        //6, 8, 3, 6, 5, 5, 0, 8, 1, 1,
        //3, 9, 4, 7, 2, 6, 9, 0, 6, 1,
        //8, 0, 2, 1, 4, 0, 6, 5, 4, 2,
        //7, 6, 2, 5, 4, 4, 6, 0, 8, 3,
        //6, 1, 2, 7, 4, 3, 0, 0, 0, 4,
        //4, 7, 8, 8, 4, 4, 4, 4, 9, 6,
        //9, 0, 2, 7, 6, 5, 9, 2, 7, 7,
        9, 7, 0, 9, 5, 4, 8, 7, 9, 9,
        2, 9, 9, 7, 0, 3, 9, 6, 8, 5,
        //5, 1, 4, 5, 5, 4, 0, 2, 6, 1,
        //5, 8, 1, 0, 1, 9, 8, 4, 8, 0,
    };
    enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };

    int data_copy[NUM_DATA];
    for (int end = 1; end < NUM_DATA; end++)
    {
        memcpy(data_copy, data, NUM_DATA * sizeof(data[0]));
        dump_data("Unsorted", data_copy, 0, end);
        quicksort(data_copy, 0, end);
        dump_data("PostSort", data_copy, 0, end);
        check_sorted(data_copy, 0, end);
    }

    return 0;
}

check_sorted() 函数通常什么都不说(但它在用于未排序的数据时确实如此):

void check_sorted(int *data, int lo, int hi)
{
    for (int i = lo + 1; i < hi; i++)
    {
        if (data[i-1] > data[i])
            printf("Inversion @ A[%d] = %d vs A[%d] = %d\n", i-1, data[i-1], i, data[i]);
    }
}

main() 程序中有 0..9 范围内的各种 20 个随机数集。


仔细重读第N维基百科页面显示:

分区: 对数组重新排序,使所有值小于枢轴的元素都在枢轴之前,而所有值大于枢轴的元素都在它之后(相等的值可以去任何一种方式) .

此尾端评论的先前版本担心有时会出现在分区数据的下半部分中的枢轴值,但指出排序仍然有效。引用表明这是完全允许的——没有问题。有一些分区方案,例如 fat pivot 方案,这是不正确的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-13
    • 1970-01-01
    • 2015-02-11
    • 2013-03-31
    • 1970-01-01
    • 2019-11-20
    • 2018-11-09
    • 1970-01-01
    相关资源
    最近更新 更多