本文介绍两种方法,因为第一种方法相对来说,易于理解,故重点在方法二的说明上。
快速排序是利用分治思想实现的一种排序方法,该方法的大致思路是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
实现方法一:挖坑填数
具体思路可以参考:https://blog.csdn.net/morewindows/article/details/6684558 (51w阅读量)
//方法一:挖坑填数+分治法
#include <iostream>
using namespace std;
#define ElementType char
void showFunc(ElementType a[], int N)
{
for (int i = 0; i < N; i++)
cout << a[i] << ' ';
cout << '\n';
}
//挖坑填数+分治法
void QuickSortDig(ElementType a[],int left,int right)
{
if (left < right)
{
int i = left, j = right;
ElementType x = a[left]; //选择最左边的数作为枢纽元
while (i < j)
{
while (i < j && a[j] >= x) //先从右边找到一个小于等于x的元素
j--;
if (i < j) //将右侧小元素填到左侧坑中
a[i++] = a[j];
while (i < j && a[i] <= x) //从左边找到一个大于等于
i++;
if (i < j) //将左边大元素填到右侧新生成坑中
a[j--] = a[i];
} //该方法i和j一定为相遇的。因为先比较元素大小再改变左右指针
a[j] = x; //或者a[i] = x;
QuickSortDig(a,left,j-1);
QuickSortDig(a, j + 1, right);
}
}
void QuickSort(ElementType a[], int N)
{
QuickSortDig(a, 0, N - 1);
}
int main()
{
ElementType a[] = "SORTEXAMPLE";
int len = strlen(a); //当数组作为函数参数传递时,数组名代表的是数组的首址,而非数组内容,故无法使用sizeof和strlen;
cout << "Raw Seq:\n";
showFunc(a, len);
cout << "Sorted Seq:\n";
QuickSort(a, len);
showFunc(a, len);
cin.get();
return 0;
}
实现方法二:基于三数中值分割方法
具体原理可参看图解:https://www.cnblogs.com/chengxiao/p/6262208.html
本实现主要依据《数据结构与算法分析 C语言描述》,有以下优点:
1、枢纽元(或称基准元数)的选择,三数中值分割法,采用一种已知且安全的分割方法,
具体见ElementType Median3(ElementType a[], int left, int right);
2、左右指针的值遇到等于枢纽元的情况下,使i和j均停止并交换,这样保证i和j会在中间相遇,避免分割出现偏向一方的情况。
3、针对小数组情况,使用插入排序,减少递归深度,同时在采用三数中值分割法情况下避免有害的特殊情形即子数组只用一个或两个元素的情况。
ps: 更多实现细节标注在代码中,帮助读者理解。
#include <iostream>
using namespace std;
#define ElementType char
void exch(ElementType a[], int curIndex, int refIndex) //交换函数
{
ElementType tmp;
tmp = a[curIndex];
a[curIndex] = a[refIndex];
a[refIndex] = tmp;
}
void showFunc(ElementType a[], int N)
{
for (int i = 0; i < N; i++)
cout << a[i] << ' ';
cout << '\n';
}
void InsertSort(ElementType a[], int N) //插入排序
{
for (int i = 1; i < N; ++i)
{
for (int j = i; j >0 && (a[j] < a[j - 1]); j--)
{
exch(a, j, j - 1);
}
}
}
/*实现三数中值分割方法的快速排序
1)将a[left]、a[center]、a[right]排序;可以避免越界,省去越界的判断代码
2)将枢纽元放到a[right-1]
3)分割阶段将i初始化到left-1,将j初始化到right-2*/
ElementType Median3(ElementType a[], int left, int right)
{
int center = left+(right-left) / 2;
ElementType pivot;
if (a[left] > a[center])
exch(a,left,center);
if (a[left] > a[right])
exch(a, left, right);
if (a[center] > a[right])
exch(a, right, center);
exch(a, right - 1, center); //将枢纽元放置在right-1处。
return a[right - 1];
}
#define CUTOFF 3 //当子数组元素个数小于该数子时选择插入排序
void Qsort(ElementType a[],int left,int right)
{
if (left+CUTOFF< right) //小于三个元素则使用插入排序
{
ElementType pivot; //定义枢纽元
int i, j;
pivot = Median3(a,left,right); //存放枢纽元
i = left;
j = right - 1;
for (;;)
{
while (a[++i] < pivot){}; //i初始化到left+1;i将停留在等于枢纽元的关键字出,将其枢纽元存储在a[right-1]处,将提供一个警戒标。
while (a[--j] > pivot){}; //j初始化到right-2 ;a[left]比枢纽元小,将其作为j的警戒标记,不必担心j越界。
if (i < j)
exch(a, i, j);
else
break;
}
exch(a, i, right - 1); //将枢纽元和左子树最右侧的交换.
/*注释:为什么是i?因为枢纽元最开始存储在大元素区域,故需要将一个大元素交换过来,
而循环结束时j<=i即i的元素是大元素,故应和i交换*/
Qsort(a, left, i - 1);
Qsort(a, i + 1, right);
}
else
{
InsertSort(a+left,right-left+1); //插入排序
}
}
void QuickSort(ElementType a[],int N)
{
Qsort(a,0,N-1);
}
int main()
{
ElementType a[] = "SORTEXAMPLE";
int len = strlen(a); //当数组作为函数参数传递时,数组名代表的是数组的首址,而非数组内容,故无法使用sizeof和strlen;
cout << "Raw Seq:\n";
showFunc(a, len);
cout << "Sorted Seq:\n";
QuickSort(a,len);
showFunc(a, len);
cin.get();
return 0;
}