快速排序逻辑错误中的分区

时间:2015-02-15 06:34:29

标签: c++ sorting logic quicksort

int partition(int A[], int low, int high){
    int mid = (low+high)/2;
    int pivot = A[mid];
    while(low <= high) {
        while(low <= high && A[high] >= pivot) {
            high--;
        }
        while (low <= high && A[low] <= pivot) {
            low ++;
        }
        if(low <= high) {
            int tmp = A[low];
            A[low] = A[high]; 
            A[high] = tmp;
            high--;
           low++;
        }
    }
    return mid;
}

void quickSort(int A[], int low, int high) {
    if (low >= high) {
      return;
    }
    int ppos = partition(A, low, high);//does the swapping and returns the pivot
    quickSort(A, low, ppos-1);
    quickSort(A, ppos+1, high);
}

这是我的快速排序功能,带有分区进行交换并返回枢轴,以便快速排序用不同的中点回忆起自己。我对quicksort不太熟悉,但这就是我想出来的。

问题是它编译得很好但是当它运行时它总是崩溃。我的职能有逻辑缺陷吗?有关如何修复我的函数的任何建议,以便它可以对随机数组进行快速排序吗?

CNC中 修复了崩溃错误,但是分区不能正确地进行排序,是否有任何改变功能的建议,以便快速排序?

3 个答案:

答案 0 :(得分:1)

您的分区功能不正确。在快速排序期间,有两种主要方法可以对序列进行分区: squeeze sweep 。前者是你正在尝试的,并且很可能比后一种方法更少的掉期,代价是更复杂的算法。


扫描

我将首先向您展示更简单的 sweep 方法,因为它实际上最简单易懂。通常,该算法执行以下操作:

  1. 如果序列长度小于2,则提前退出。对于长度为1的序列没有任何划分。
  2. 在序列中选择一个透视值。选择一个可以减少O(N ^ 2)退化条件的机会的枢轴值是快速分区的圣杯,我不会在这里讨论,但是有很多的令牌该主题可在线获取。
  3. 将透视值与序列中的最后一个值交换。
  4. 使用两个索引值遍历序列;读者索引和作者索引。当您向上移动序列时,任何“小于”数据透视值的读取器索引值将被交换到writer-index的较低序列,并且编写器索引会递增。
  5. 完成后,编写器索引是将透视值交换到最终位置并且分区完成的点。返回的结果分区点是编写器索引位置。
  6. 这个算法实际上更容易用代码来理解:

    size_t partition(int A[], size_t len)
    {
        if (len < 2) // 1.
            return 0;
    
        std::iter_swap(A+len/2, A+len-1); // 2. 3.
        size_t pvt = 0;
    
        for (size_t i=0; i<len-1; ++i) // 4.
        {
            if (A[i] < A[len-1])
                std::iter_swap(A + pvt++, A+i);
        }
        std::iter_swap(A + pvt, A+len-1); // 5.
    
        return pvt;
    }
    

    请注意,完全可以想象值大于的值可能会在行军期间填充下部分区,因此可以交换多次次。最终它全部解决了,但理想情况下避免了这些额外的掉期。这就是 squeeze 方法的目的,如下所示。


    挤压

    虽然 sweep 具有其优点(最值得注意的是简单性),但最小化交换不是其中之一。理想情况下,当您确定两个值在枢轴值的最终着陆位置的相对侧不合适时,您只执行交换。要做到这一点,你需要用高到低的扫描同时执行从低到高的扫描 ,并且每当在不正确的位置找到一个元素时,交换那些。最终低指数和高指数相遇,一旦这样做,你就找到了枢轴最终安息之地。

    size_t partition(int A[], size_t len)
    {
        if (len < 2)
            return 0;
    
        std::iter_swap(A + len/2, A+len-1);
        size_t low = 0, high = len;
    
        while (1)
        {
            while (low < high && (A[low] < A[len-1]))
                ++low;
    
            if (low == high--)
                break;
    
            while (low < high && !(A[high] < A[len-1]))
                --high;
    
            if (low == high)
                break;
    
            std::iter_swap(A+low++, A+high);
        }
        std::iter_swap(A+low, A+len-1);
    
        return low;
    }
    

    这里发生的一些事情看起来很奇怪。请注意 second 内部循环的布尔逻辑,它会减少high。我本来可以写(A[high] >= A[len-1]),但我想把一个常见的错误带回家。 严重 high逻辑减少为促进low的条件。如果low被提升,因为它的元素严格小于我们在此处所拥有的数据透视值,那么high只有在其元素时才能减少(严格小于枢轴值)。毫无疑问,我们在上面编码时显示它是正确的,而我根本无法确定特定要求被掩盖的次数并导致神秘破坏的分区算法。


    使用QuickSort的示例分区

    上述任何一种都可行。在交换发生时对函数进行一些微小的修改以产生输出并对随机改组的值数组进行排序表明交换计数减少。以下实现了两个分区函数中的两种算法,分别标记为sweepsqueeze。它们在相同的随机序列上都松散,然后在完全分类的序列上再次展开,以证明交换计数差异。

    #include <iostream>
    #include <algorithm>
    #include <random>
    #include <numeric>
    
    size_t sweep(int A[], size_t len)
    {
        if (len < 2)
            return 0;
    
        std::iter_swap(A+len/2, A+len-1);
        size_t pvt = 0;
    
        for (size_t i=0; i<len-1; ++i)
        {
            if (A[i] < A[len-1])
            {
                std::cout << "swap: " << A[pvt] << ',' << A[i] << '\n';
                std::iter_swap(A + pvt++, A+i);
            }
        }
        std::iter_swap(A + pvt, A+len-1);
    
        return pvt;
    }
    
    size_t squeeze(int A[], size_t len)
    {
        if (len <= 1)
            return 0;
    
        std::iter_swap(A + len/2, A+len-1);
        size_t low = 0, high = len;
    
        while (1)
        {
            while (low < high && A[low] < A[len-1])
                ++low;
    
            if (low == high--)
                break;
    
            while (low < high && !(A[high] < A[len-1]))
                --high;
    
            if (low == high)
                break;
    
            std::cout << "swap: " << A[low] << ',' << A[high] << '\n';
            std::iter_swap(A+low++, A+high);
        }
        std::iter_swap(A+low, A+len-1);
    
        return low;
    }
    
    void quicksort(int A[], size_t len, size_t (*part)(int[], size_t))
    {
        if (len < 2)
            return;
    
        size_t pvt = part(A, len);
        quicksort(A, pvt++, part);
        quicksort(A+pvt, len-pvt, part);
    }
    
    int main()
    {
        std::random_device rd;
        std::mt19937 rng(rd());
    
        int ar[31] = {0}, ar2[31];
        std::iota(std::begin(ar), std::end(ar), 1);
        std::shuffle(std::begin(ar), std::end(ar), rng);
        std::copy(std::begin(ar), std::end(ar), std::begin(ar2));
    
        for (auto x : ar)
            std::cout << x << ' ';
        std::cout << '\n';
    
        std::cout << "Sweep Algorithm\n";
        quicksort(ar, sizeof(ar)/sizeof(*ar), sweep);
    
        for (auto x : ar)
            std::cout << x << ' ';
        std::cout << '\n';
    
        std::cout << "Squeeze Algorithm\n";
        quicksort(ar2, sizeof(ar2)/sizeof(*ar2), squeeze);
    
        for (auto x : ar2)
            std::cout << x << ' ';
        std::cout << '\n';
    
        std::cout << "Sweep Algorithm (sorted)\n";
        quicksort(ar, sizeof(ar)/sizeof(*ar), sweep);
    
        for (auto x : ar)
            std::cout << x << ' ';
        std::cout << '\n';
    
        std::cout << "Squeeze Algorithm (sorted)\n";
        quicksort(ar2, sizeof(ar2)/sizeof(*ar2), squeeze);
    
        for (auto x : ar2)
            std::cout << x << ' ';
        std::cout << '\n';
    }
    

    输出(随机)

    8 28 21 26 10 12 17 1 11 20 30 3 18 5 24 15 9 6 13 27 31 4 16 7 19 22 14 25 29 2 23 
    Sweep Algorithm
    swap: 8,8
    swap: 28,10
    swap: 21,12
    swap: 26,1
    swap: 28,11
    swap: 21,3
    swap: 17,5
    swap: 26,9
    swap: 28,6
    swap: 20,13
    swap: 30,4
    swap: 21,7
    swap: 18,14
    swap: 17,2
    swap: 8,8
    swap: 10,1
    swap: 12,3
    swap: 10,5
    swap: 11,2
    swap: 12,6
    swap: 10,4
    swap: 11,7
    swap: 8,1
    swap: 3,3
    swap: 5,5
    swap: 7,4
    swap: 3,3
    swap: 4,4
    swap: 3,3
    swap: 7,7
    swap: 13,10
    swap: 12,12
    swap: 13,13
    swap: 12,12
    swap: 23,20
    swap: 26,16
    swap: 28,19
    swap: 23,18
    swap: 27,17
    swap: 20,16
    swap: 20,17
    swap: 20,18
    swap: 16,16
    swap: 30,22
    swap: 24,24
    swap: 30,26
    swap: 31,28
    swap: 30,27
    swap: 26,26
    swap: 27,27
    swap: 26,26
    swap: 30,30
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
    Squeeze Algorithm
    swap: 28,2
    swap: 21,14
    swap: 26,7
    swap: 17,4
    swap: 20,13
    swap: 30,6
    swap: 18,9
    swap: 14,3
    swap: 7,4
    swap: 14,9
    swap: 12,6
    swap: 30,25
    swap: 27,21
    swap: 31,22
    swap: 23,16
    swap: 25,17
    swap: 30,28
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
    Sweep Algorithm (sorted)
    swap: 1,1
    swap: 2,2
    swap: 3,3
    swap: 4,4
    swap: 5,5
    swap: 6,6
    swap: 7,7
    swap: 8,8
    swap: 9,9
    swap: 10,10
    swap: 11,11
    swap: 12,12
    swap: 13,13
    swap: 14,14
    swap: 15,15
    swap: 1,1
    swap: 2,2
    swap: 3,3
    swap: 4,4
    swap: 5,5
    swap: 6,6
    swap: 7,7
    swap: 1,1
    swap: 2,2
    swap: 3,3
    swap: 1,1
    swap: 5,5
    swap: 9,9
    swap: 10,10
    swap: 11,11
    swap: 9,9
    swap: 13,13
    swap: 17,17
    swap: 18,18
    swap: 19,19
    swap: 20,20
    swap: 21,21
    swap: 22,22
    swap: 23,23
    swap: 17,17
    swap: 18,18
    swap: 19,19
    swap: 17,17
    swap: 21,21
    swap: 25,25
    swap: 26,26
    swap: 27,27
    swap: 25,25
    swap: 29,29
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
    Squeeze Algorithm (sorted)
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
    

    祝你好运。

答案 1 :(得分:0)

您似乎正在返回数据透视表的值,而不是它在分区函数中的位置。

答案 2 :(得分:0)

以下是使用分区索引的实现:

void swap(int A[], int i1, int i2){
    int temp=A[i1];
    A[i1]=A[i2];
    A[i2]=temp;
}

int partition(int A[], int low, int high){
    int partitionIndex, i, pivot;

    pivot = A[high];
    partitionIndex=low;

    for(i=low; i < high; i++){
        if(A[i]<=pivot){
            swap(A,i,partitionIndex);
            partitionIndex++;
        }
    }
    swap(A,high,partitionIndex);
    return partitionIndex;
}

void quickSort(int A[], int low, int high) {
    if (low < high){
        int ppos = partition(A, low, high);//does the swapping and returns the pivot
        quickSort(A, low, ppos-1);
        quickSort(A, ppos+1, high);
    }
}

我无法弄清楚你的代码有什么问题。在调试过程中,我注意到您编辑了问题中的代码。希望上面的代码可以帮助你解决问题。