一、算法描叙
快速排序是对冒泡排序的一种改进。其基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,接着在分别对这两部分记录继续进行排序,以达到整个序列有序。
快速排序的思想其实是分治思想的模式,将一个序列分成两部分然后分别对每一部分作递归处理;下面用一个例子来明了的描叙这一过程:
Ps:习惯上以第一个记录作为枢轴记录,即49作为枢轴关键字(下图红色表示),附设low和high指针(下图分别用蓝色和红色表示,绿色表示两指针重合的位置),初始分别指向第一个和最后一个记录。
具体见下:
初始序列:49 38 65 97 76 13 27
1 次交换后:27 38 65 97 76 13 65
2 次交换后:27 38 13 97 76 97 65
3 次交换后:27 38 13 76 49 97 65 //当left==right时,将枢轴关键字49填充到left(right)位置
完成一趟排序:27 38 13 49 76 97 65
有时大家会看到的排序是以下过程:
初始序列:49 38 65 97 76 13 27
进行第一次交换后: 27 38 65 97 76 13 49
进行第二次交换后: 27 38 49 97 76 13 65
进行第三次交换后: 27 38 13 97 76 49 65
进行第四次交换后: 27 38 13 49 76 97 65
完成一趟排序:27 38 13 49 76 97 65
在这里,这两种排序思想还是没变的,只不过在每一次的记录移动(赋值)时存在区别,后者是在每交换一次记录需进行3次记录移动,但实际上这一过程是可以简化的。因为每一次的记录移动赋值操作包括枢轴记录在内,而枢轴记录的最终位置是在left=right的位置上,所以在每一次的交换过程中,可以将枢轴记录暂存于某一位置。 right指针先向前查找第一个比49小或等于的数(即27),赋值给left指针位置,再由left指针向后查找到第一个比49大或等于的数,再将该数填充到right空出的位置,完成一次交换,以此类推!
二、算法分析
1)平均时间复杂度:(n*lg n)
最好情况:快速排序每一次的过程是将记录分成两个部分,在最理想的情况下,每一次的交换是将待排记录划分成两个等长的记录,而n个数则需进行lgn层的递归深度,而每一层需对n个记录作一次处理,即最优时间复杂度:T(n*lgn);
最坏情况:当待排序列为有序时,每次划分只得到一个比上一次划分少一个记录的子序列,快速排序退化为冒泡排序。时间复杂度为T(n^2);
2)空间复杂度: O (lgn);
3)稳定性:不稳定;
对于快排的算法分析,可以参照这篇文章,写的很详细:http://book.51cto.com/art/201108/287089.htm
三、示例源码
#include<iostream>
using namespace std;
void QuickSort(int *a,int *low,int *high)
{
int i,j;
int temp=*low; //用temp记录枢轴关键字 ,即子表的第一个记录作为枢轴关键字
int *first=low; //用first、last两个变量分别记录整个记录表的最低、最高位 ,固定不变
int *last=high;
while (low != high)
{
while(*high >= temp && low < high) //high指针向前查找,将比枢轴记录小的记录移到(赋值)低端 low位置
high--;
*low=*high;
while (*low <= temp && low < high) //low指针向后查找,将比枢轴记录大的记录移到(赋值)高端 high位置
low++;
*high=*low;
}
*low= temp; //一趟排序后,当low=high时,将枢轴记录移到low(high)位置
QuickSort(a,first,low-1); //一趟排序后,对枢轴左边待排记录递归排序
QuickSort(a,low+1,last); //一趟排序后,对枢轴右边待排记录递归排序
}
int main ()
{
int a[]={49,38,65,97,76,13,27};
int len = sizeof(a)/sizeof(int);
QuickSort(a,a,&a[len-1]);
for (int i=0;i<len;i++)
cout << a[i]<<" ";
cout << endl;
return 0;
}