【问题标题】:C++ QuickSort Algorithm keeps crashingC ++快速排序算法不断崩溃
【发布时间】:2014-11-30 07:36:41
【问题描述】:

尝试实现快速排序算法。我认为问题出在递归上,但我不知道该怎么做才能解决它。每次我运行该程序都会继续崩溃,我不明白为什么。代码如下:

#include<iostream>

using namespace std;
int pIndex;

int Partition(int *A,int start,int end){

int pivot;
int pIndex;
 if(start<end){
    int pivot=A[end];
    int pIndex=start;
    for(int x=0;x<end;x++){
        if(A[x]<A[end]){
            swap(A[x],A[pIndex]);
             pIndex=pIndex+1;   
        }   
    }
    swap(A[pIndex],A[end]);
 }
//cout<<pIndex<<endl;
swap(A[pIndex],A[end]);
return pIndex;
};
void QuickSort(int *A, int start,int end){
    if(start<end)
    {

    pIndex=Partition(A,start,end);
    QuickSort(A,pIndex+1,end);
    QuickSort(A,start,pIndex-1);
}
};
int main(){

int A[10]{4,23,1,43,2,10};  
//Partition(A,0,9);
QuickSort(A,0,5);
for(int x=0;x<10;x++){
    cout<< A[x]<<" ";
}
}

【问题讨论】:

  • 你试过在调试器下运行程序吗?它在哪一行显示崩溃?
  • 您不应该在函数末尾的} 之后放置分号;它是一个空语句或空声明。但是,这与您的崩溃完全无关。
  • 给出的答案解决了你的崩溃,但是,我认为你必须重新审视你的算法逻辑,因为分区永远不会像你期望的那样工作。

标签: c++ sorting loops recursion quicksort


【解决方案1】:

您的分区算法大约是所需代码的两倍。您似乎总是选择序列中的 last 元素作为枢轴,虽然不建议这样做,但它可以用于学术演示。

你的崩溃

您定义了两个pIndex 值,其中只有一个实际上是确定性的。您还声明了两个 pivot 变量,但这不会导致您的崩溃(第一个从未使用过)。它应该被清理干净,但是你代码中的丧钟是重复的pIndex

int pivot;
int pIndex;     // HERE
if(start<end){
    int pivot=A[end];
    int pIndex=start; // HERE AGAIN
    for(int x=0;x<end;x++){
        if(A[x]<A[end]){
            swap(A[x],A[pIndex]);
            pIndex=pIndex+1;   
        }   
    }
    swap(A[pIndex],A[end]);
}
swap(A[pIndex],A[end]); // uses non-determined pIndex
return pIndex; // returns non-determined pIndex

int pIndex=start; 更改为pIndex=start; 将解决您的崩溃问题,但您的分区方法仍然需要...帮助。


“扫描”分区方法

对于假设为右尾的枢轴值,“扫描”分区方法通常是这样完成的,您将很难得到这个更简单的方法(调用std::partition 无法承受):

size_t Partition(int *A, size_t len)
{
    if (len < 2)
        return 0;

    size_t pvt = 0;
    for (size_t i=0; i<end; ++i)
    {
        if (A[i] < a[len-1])
            std::swap(A[i], A[pvt++])
    }
    std::swap(A[pvt], a[len-1]);
    return pvt;
};

上述算法仅包括分区所需的必要条件:序列迭代器(在您的情况下为指针)和序列长度。其他一切都是基于这两个项目的确定性。下面是一个快速示例程序,它是如何工作的,特意放置了 5 作为枢轴值:

#include <iostream>

size_t Partition(int *A, size_t len)
{
    if (len < 2)
        return 0;

    size_t pvt = 0;
    for (size_t i=0; i<len-1; ++i)
    {
        if (A[i] < A[len-1])
            std::swap(A[i], A[pvt++]);
    }
    std::swap(A[pvt], A[len-1]);
    return pvt;
};

int main()
{
    int arr[] = { 4, 8, 7, 3, 9, 2, 1, 6, 5 };
    size_t n = Partition(arr, sizeof(arr)/sizeof(*arr));
    std::cout << "Partition : " << n << '\n';
    for (auto x : arr)
        std::cout << x << ' ';
    std::cout << '\n';
}

输出

Partition : 4
4 3 2 1 5 7 8 6 9 

如何从 QuickSort 调用

在快速排序中调用分区设置枢轴位置,该位置成为底部段的“结束”迭代点,以及顶部段的 one-BEFORE 迭代点。 关键Partition() 调用返回的枢轴位置在递归时不应包含在 either 子序列中。

void QuickSort(int *A, size_t len)
{
    if (len < 2)
        return;

    size_t pvt = Partition(A, len);
    QuickSort(A, pvt++);        // NOTE: post increment...
    QuickSort(A+pvt, len-pvt);  // ...which makes this skip the pivot
}

是的,指针算术很厉害,你不觉得吗?


把它们放在一起

下面的程序包含两个 PartitionQuickSort

#include <iostream>

size_t Partition(int *A, size_t len)
{
    if (len < 2)
        return 0;

    size_t pvt = 0;
    for (size_t i=0; i<len-1; ++i)
    {
        if (A[i] < A[len-1])
            std::swap(A[i], A[pvt++]);
    }
    std::swap(A[pvt], A[len-1]);
    return pvt;
};

void QuickSort(int *A, size_t len)
{
    if (len < 2)
        return;

    size_t pvt = Partition(A, len);
    QuickSort(A, pvt++);            // NOTE: post increment
    QuickSort(A+pvt, len-pvt);
}

int main()
{
    int arr[] = { 4, 8, 7, 3, 9, 2, 1, 6, 5 };
    QuickSort(arr, sizeof(arr)/sizeof(*arr));
    for (auto x : arr)
        std::cout << x << ' ';
    std::cout << '\n';
}

输出

1 2 3 4 5 6 7 8 9 

希望对你有帮助。

【讨论】:

  • 非常感谢,这解决了问题。感谢您在帖子中所做的所有工作:) 谢谢
【解决方案2】:

我也是一个 C++ 新手,但我发现自己很好奇 start >= end 时会发生什么。看起来您的 Partition 函数仍然会返回一个 pIndex 值,但我看不到您在哪里定义它。如果(我怀疑)它返回恰好驻留在内存中的任何值,那么当您使用 A[pIndex]

时,您很可能最终会引用一些未定义的内存位置

【讨论】:

  • 您对pIndex 从未被设置的观察是准确的。
【解决方案3】:

在这部分:

int pivot;
int pIndex;
 if(start<end){
    int pivot=A[end];
    int pIndex=start;

您正在定义两个枢轴和两个 pIndex。您根本没有使用枢轴,并且在最后一次交换中您使用的是未初始化的 pIndex。这应该有效:

int Partition(int *A,int start,int end){    
    int pIndex = start;
    if(start<end){
        for(int x=0;x<end;x++){
            if(A[x]<A[end]){
                swap(A[x],A[pIndex]);
                pIndex=pIndex+1;   
            }
        }
        swap(A[pIndex],A[end]);
    }   
    swap(A[pIndex],A[end]);
    return pIndex;
}

【讨论】:

  • 那行不通。输出为23 10 2 43 4 1 0 0 0 0(虽然至少不会崩溃)
  • 当我替换这个函数时,它确实修复了崩溃。但屏幕上绝对没有出现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-17
相关资源
最近更新 更多