编写一份快速排序,完成对元素序列37,19,43,22,22,89,26,92按照从小到大顺序排列。
【算法思想】
快速排序是冒泡排序算法的改进,也属于交换类型的排序算法,它的基本思想描述如下:
假设待排序元素个数为n,分别存放在数组a[1,...,n]中,令第1 个元素为参考元素(枢轴元素),pivot=a[1]。初始时,i=1,j=n,然后按照以下方法操作:
(1)从第j个元素开始向前依次将每个元素与枢轴元素pivot比较。如果当前元素大于pivot,则比较前一个元素与pivot,即比较a[j-1]与pivot;否则,将当前元素移动到第i个位置并转步骤(2)执行。
(2)从第i个元素开始向后依次将每个元素与枢轴元素pivot比较。如果当前元素小于pivot,则比较后一个元素与pivot,即比较a[i+1]与pivot;否则,将当前元素移动到第j个位置并转步骤(3)执行。
(3)重复执行(1)和(2),直到i≥j,将元素pivot移动到a[i]中。此时整个元素序列被划分两个部分:小于a[i]元素位于第i个位置之前,大于等于a[i]的元素位于第i个位置之后。这样就完成了一趟快速排序,即一次划分。
按照以上方法,对于每个部分(子表)进行类似的划分操作,直到每个子表只有一个元素为止,这样整个序列就构成了一个有序的序列。
例如,一个元素序列为37,19,22,22,89,26,92,根据快速排序算法思想,第一次划分过程如下图所示。
从上图可以看出,当一趟快速排序完毕后,整个元素序列被枢轴元素的关键字37划分为两个子表,左边子表的元素值都小于37,右边子表的元素值大于等于37。使用快速排序对前面的元素序列进行排序的整个过程如下图所示。
通过上面的排序过程不难看出,快速排序算法可以通过递归调用实现,排序过程其实是不断地对元素序列尽心划分,直到每一个部分不能划分时,即完成快速排序。
code:
#include<stdio.h>
void DispArray(int a[], int n);
void DispArray2(int a[], int n, int pivot, int count);
void QSort(int a[], int n, int low, int high);
void QuickSort(int a[], int n);
int Partition(int a[], int low, int high);
void QSort(int a[], int n, int low, int high)
/*利用快速排序算法对数组a中的元素排序*/
{
int pivot;
int static count = 1;
if (low<high) /*如果元素序列的长度大于1*/
{
pivot = Partition(a, low, high);/*将待排序序列a[low..high]划分为两部分*/
DispArray2(a, n, pivot, count); /*输出每次划分的结果*/
count++;
QSort(a, n, low, pivot - 1); /*对左边的子表进行递归排序,pivot是枢轴位置*/
QSort(a, n, pivot + 1, high); /*对右边的子表进行递归排序 */
}
}
void QuickSort(int a[], int n)
/*对数组a进行快速排序*/
{
QSort(a, n, 0, n - 1);
}
int Partition(int a[], int low, int high)
/*对数组a[low..high]的元素进行一趟排序,使枢轴前面的元素小于枢轴元素,枢轴后面的元素大于等于枢轴元素,并返回枢轴位置*/
{
int t, pivot;
pivot = a[low]; /*将表的第一个元素作为枢轴元素*/
t = a[low];
while (low<high) /*从表的两端交替地向中间扫描*/
{
while (low<high&&a[high] >= pivot)/*从表的末端向前扫描*/
high--;
if (low<high) /*将当前high指向的元素保存在low位置*/
{
a[low] = a[high];
low++;
}
while (low<high&&a[low] <= pivot)/*从表的始端向后扫描*/
low++;
if (low<high) /*将当前low指向的元素保存在high位置*/
{
a[high] = a[low];
high--;
}
a[low] = t; /*将枢轴元素保存在low的位置*/
}
return low; /*返回枢轴所在位置*/
}
void DispArray2(int a[], int n, int pivot, int count)
/*输出每次划分的结果*/
{
int i;
printf("第%d次划分结果:[", count);
for (i = 0; i<pivot; i++)
printf("%-4d", a[i]);
printf("]");
printf("%3d ", a[pivot]);
printf("[");
for (i = pivot + 1; i<n; i++)
printf("%-4d", a[i]);
printf("]");
printf("\n");
}
void main()
{
int a[] = { 37,19,43,22,22,89,26,92 };
int n = sizeof(a) / sizeof(a[0]);
printf("快速排序前:");
DispArray(a, n);
QuickSort(a, n);
printf("快速排序结果:");
DispArray(a, n);
getchar();
}
void DispArray(int a[], int n)
/*输出数组中的元素*/
{
int i;
for (i = 0; i<n; i++)
printf("%4d", a[i]);
printf("\n");
}
result:
【主要用途】
快速排序是冒泡排序算法的改进,实现比较复杂,它的主要用途是在需要对大量数据尽心排序的情况,它的时间效率要远高于冒泡排序,在处理数据量特别大的情况下效果明显。
【稳定性和复杂度】
快速排序是一种不稳定的排序算法,在最好的情况下,每趟排序都是将元素序列正好划分为两个等长的子序列。这样,快速排序子序列的划分过程就是创建完全二叉树的过程,划分的次数等于树的深度,即log2 n,因此快速排序总的比较次数为 T(n)≤n+2T(n/2)≤n+2*(n/2+2*T(n/4))=2n+4T(n/4)≤3n+8T(n/8)≤...≤nlog2 n+nT(1)。因此在最好的情况下时间复杂度为O(nlogn)
在最坏的情况下,待排序元素已经是有序的,则时间的花费主要集中在元素的比较次数上。第一趟需要比较n-1次,第二趟需要比较n-2次,以此类推,共需要比较n(n-1)/2次,因此时间复杂度为O(n^2)。
平均情况下,快速排序的时间负责度为O(nlog2 n),空间复杂度为O(log2 n)