快速排序程序停止工作

时间:2016-12-02 07:56:37

标签: c++ sorting quicksort

我正试图解决对黑客的快速排序 - 2挑战。它说我们必须反复调用分区,直到整个数组都被排序。我的程序适用于某些测试用例,但对于某些测试用例,它会崩溃,快速排序 - 2.exe已停止工作"。我找不到它为什么会发生的原因。 数组/子数组的第一个元素每次都被视为数据元素。

#include <iostream>
#include <conio.h>

using namespace std;

void swap(int arr[], int a, int b)
{
    int c = arr[a];
    arr[a] = arr[b];
    arr[b] = c;
}

void qsort(int arr[], int m, int n)    //m - lower limit, n - upper limit
{
    if (n - m == 1)
    {
        return;
    }

    int p = arr[m], i, j, t;           //p - pivot element, t - temporary
    //partition
    for (int i = m+1; i < n; i++)
    {
        j = i;
        if (arr[j] < p)
        {
            t = arr[j];
            while (arr[j] != p)
            {
                arr[j] = arr[j-1];
                j--;
            }
            arr[j] = t;                //pivot is at j and j+1
        }
    }
    //check if sorted
    int f = 1;
    while (arr[f] > arr[f-1])
    {
        if (f == n-1)
        {
            f = -1;
            break;
        }
        f++;
    }
    if (f == -1)
    {
        cout << "Sub Array Sorted\n";
    }
    else
    {
        if (p == arr[m])               //pivot is the smallest in sub array
        {
            qsort(arr, m+1, n);        //sort right sub array
        }
        else
        {
            qsort(arr, m, j+1);        //sort left sub array
            qsort(arr, j+1, n);        //sort right sub array
        }
    }
}

int main()
{
    int n;
    cin >> n;

    int arr[n];
    for (int i = 0; i < n; i++)
    {
        cin >> arr[i];
    }

    qsort(arr, 0, n);

    for (int i = 0; i < n; i++)
    {
        cout << arr[i] << " ";
    }

    return 0;
}

2 个答案:

答案 0 :(得分:0)

您的索引超出范围问题。

这不会为您提供解决方案,但它可以帮助您找到程序失败的原因。

我修改了你的程序,因此它使用vector int而不是int的原始数组,当你运行这个程序时,你会得到一个超出范围异常的索引。

触发问题的序列4 3 7 1 6 4是硬编码的,因此您不需要每次都输入它。

#include <iostream>
#include <vector>

using namespace std;

void swap(vector<int> & arr, int a, int b)
{
  int c = arr[a];
  arr[a] = arr[b];
  arr[b] = c;
}

void qsort(vector<int> & arr, int m, int n)    //m - lower limit, n - upper limit
{
  if (n - m == 1)
  {
    return;
  }

  int p = arr[m], j, t;           //p - pivot element, t - temporary
                                     //partition
  for (int i = m + 1; i < n; i++)
  {
    j = i;
    if (arr[j] < p)
    {
      t = arr[j];
      while (arr[j] != p)
      {
        arr[j] = arr[j - 1];
        j--;
      }
      arr[j] = t;                //pivot is at j and j+1
    }
  }
  //check if sorted
  int f = 1;
  while (arr[f] > arr[f - 1])
  {
    if (f == n - 1)
    {
      f = -1;
      break;
    }
    f++;
  }
  if (f == -1)
  {
    cout << "Sub Array Sorted\n";
  }
  else
  {
    if (p == arr[m])               //pivot is the smallest in sub array
    {
      qsort(arr, m + 1, n);        //sort right sub array
    }
    else
    {
      qsort(arr, m, j + 1);        //sort left sub array
      qsort(arr, j + 1, n);        //sort right sub array
    }
  }
}

int main()
{
  vector<int> arr = { 4,3,7,1,6,4 };

  qsort(arr, 0, arr.size());

  for (unsigned int i = 0; i < arr.size(); i++)
  {
    cout << arr[i] << " ";
  }

  return 0;
}

答案 1 :(得分:0)

首先,你所做的不是快速排序,而是分裂 - 征服分区和插入排序的某种组合。

Canonical quicksort来自数组的下(p)和上(q)边界,分别跳过元素arr [p] m。然后用arr [q]交换arr [p],递增/递减并检查p> = q。冲洗并重复直至p> = q。然后在子分区上进行调用。这种方式p或q保持枢轴位置和子弹很明显。

但是你的方式不同:你从子阵列的右侧向左侧插入元素。这样的事情可以产生一次迭代的O(N ^ 2)时间复杂度。例如,考虑1,0,1,0,1,0,1,0,1,0,1,0,...序列。这可能会增加O(N ^ 2)的最坏情况复杂性。

时间复杂度...函数中的问题在于假设j在子查询中保存了枢轴位置:

qsort(arr, m, j+1);        //sort left sub array
qsort(arr, j+1, n);        //sort right sub array

实际上,j在你的main for循环中一次又一次地设置为等于i。如果最后一个元素等于或大于pivot,你最终得到j = n-1,你调用qsort(arr,n,n)并传递第一行检查(sic!),因为nn!= 1。

要解决此问题,您应该做两件事:

1)重新排列后直接找到枢轴位置:

for (int i = m; i < n; i++)
    if (p == arr[i])
    {
        j = i;
        break;
    }

或在不同变量中初始化,在此行之后更新:

arr[j] = t;                //pivot is at j and j+1

并更新递归调用以使用新变量而不是j

2)在你的功能开始时做一个更加防弹的检查:

if (n - m <= 1)

后者足以得到一些结果,但它会比你目前的想法低得多,在最坏的情况下可能会下降到O(N ^ 3)。