今天在写快速排序代码的时候出现了错误,才发现一直忽略了快排的这一点,错误代码如下:
#include <iostream>
void Swap(int* left, int* right)
{
int temp = *left;
*left = *right;
*right = temp;
}
void Print(int* array, int size)
{
for (int i = 0; i < size; ++i)
{
std::cout << array[i] << " ";
}
std::cout << std::endl;
}
int Partion(int* array, int left, int right)
{
//hoare版
int begin = left;
int end = right - 1;//左闭右开区间
int pivot = array[begin];
while (begin < end)
{
while (begin < end && array[begin] <= pivot)
{
++begin;
}
while (begin < end && array[end] >= pivot)
{
--end;
}
if (begin < end)
{
Swap(&array[begin], &array[end]);
}
}
if (begin != left)
{
Swap(&array[begin], &array[left]);
}
return begin;
}
void QuickSort(int* array, int left, int right)
{
if (right - left <= 1)
{
return;
}
int div = Partion(array, left, right);
QuickSort(array, left, div);
QuickSort(array, div + 1, right);
}
int main()
{
int array[] = { 6,1,2,5,3,0,4,8,7,9 };
int arrSize = sizeof(array) / sizeof(array[0]);
std::cout << "排序前: " << std::endl;
Print(array, arrSize);
QuickSort(array,0, arrSize);
std::cout << "排序后: " << std::endl;
Print(array, arrSize);
return 0;
}
在写的时候也没有发现什么错误,没有警告,但是运行后结果不对,截图如下:
百思不得其解,最后对照以前的代码发现了,发现是在找比基准元素大/小的方向错了!!
while (begin < end && array[begin] <= pivot)
{
++begin;
}
while (begin < end && array[end] >= pivot)
{
--end;
}
上面就是错误代码区域,修改的话只需要交换两个循环位置。为什么开始的方向会影响结果呢?
本文以数组最左侧元素为基准值,错误代码中先从左向右查找比基准值大的元素,一趟比较之后出现以下状况:
这时候执行Swap(&array[begin], &array[left]);
交换后数组变为 8 1 2 5 3 0 4 6 7 9很显然6的左边并不全部比6小。
主要原因是(基准值为左侧第一个数时)先从左边开始移动begin++,当结束循环(begin于end相等)时,会出现最后结束位置的值比基准值大(8 > 6)的情况; 但是如果最开始从右边向左移动end–,当结束循环时所在结束位置的值一定是小于基准值的:
- 要么
array[begin],array[end]
交换后,直到end到达begin位置,但是之前已经交换了,所以begin位置一定小于基准值 - 要么交换
array[begin],array[end]
后(或者一次也没有交换) end又找到比基准值小的元素,但是begin没有找到比基准值大的元素,直到begin与end相等时结束循环,此时begin和end同一位置,所在元素一定小于基准值
这时交换后就一定能保证基准值左边全是小于基准值,右边全是大于基准值的。
修改后代码如下:
#include <iostream>
void Swap(int* left, int* right)
{
int temp = *left;
*left = *right;
*right = temp;
}
void Print(int* array, int size)
{
for (int i = 0; i < size; ++i)
{
std::cout << array[i] << " ";
}
std::cout << std::endl;
}
int Partion(int* array, int left, int right)
{
//hoare版
int begin = left;
int end = right - 1;//左闭右开区间
int pivot = array[begin];
while (begin < end)
{
while (begin < end && array[end] >= pivot)
{
--end;
}
//先后顺序要注意
while (begin < end && array[begin] <= pivot)
{
++begin;
}
if (begin < end)
{
Swap(&array[begin], &array[end]);
}
}
if (begin != left)
{
Swap(&array[begin], &array[left]);
}
return begin;
}
void QuickSort(int* array, int left, int right)
{
if (right - left <= 1)
{
return;
}
int div = Partion(array, left, right);
QuickSort(array, left, div);
QuickSort(array, div + 1, right);
}
int main()
{
int array[] = { 6,1,2,5,3,0,4,8,7,9 };
int arrSize = sizeof(array) / sizeof(array[0]);
std::cout << "排序前: " << std::endl;
Print(array, arrSize);
QuickSort(array,0, arrSize);
std::cout << "排序后: " << std::endl;
Print(array, arrSize);
return 0;
}
结果如下:
当然:以最右侧元素为基准值时,要先从左边开始向右找比基准值小的元素位置。