1、快速排序
---- 快速排序(Quicksort)是对冒泡排序的一种改进,由C.A.R.Hoare在1962年提出。
它的基本思想是:通过“一趟排序”将要排序的数据分割成独立的两部分,其中左部分的所有数据都比右部分的所有数据都要小,然后再按此方法
对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
2、算法过程
---- 设要排序的数组是A[0]......A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比
它大的数都放到它后面,这个过程称为一趟排序。
值得注意的是:快速排序不是一种稳定的排序算法,即多个相同的值的相对位置也许会在算法结束时产生变动。一趟快速排序的算法是:
----1)设置两个变量i,j,排序开始的时候:i=0,j=N-1;
----2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];注意该key值在整个过程中永远不变,始终是和key进行比较。
----3)从j开始由后向前搜索,找到第一个小于key的值A[j],并与A[i]交换;
----4)从i开始由前向后搜索,找到第一个大于key的值A[i],并与A[j]交换;
----5)重复第3、4步,直到i=j;
注意:3)4)步是在程序中没找到时候才j--和i++,直到找到为止。找到并交换的时候i和j指针位置不变。
#include <iostream>
using namespace std;
#define dim(x) (sizeof(x)/sizeof(x[0]))
void swap(int *x,int *y)
{
int t = *x;
*x = *y;
*y = t;
}
//输入a[]:要排序的数组 nMin:第一个下标 nMax:最后一个下标
//输出:int-基准所在下标位置
//功能:分离数组并就地排序
int Partition(int a[],int nMin,int nMax)
{
int key = a[nMin];
int i = nMin;
int j = nMax;
do{
//由后向前找到第一个比key小的元素,并与a[i]交换
while(a[j]>=key && i<j)
{
j--;
}
if(i<j) swap(&a[i],&a[j]);
while(a[i]<=key && i<j)
{
i++;
}
if(i<j) swap(&a[i],&a[j]);
}while(i<j);
return j;
}
void Quicksort(int a[],int nMin,int nMax)
{
if(nMin>=nMax) return;
if(nMin+1 == nMax) //若只有两个元素,直接比较
{
if(a[nMin]>a[nMax])
swap(&a[nMin],&a[nMax]);
return;
}
int j = Partition(a,nMin,nMax);
Quicksort(a,nMin,j-1);
Quicksort(a,j+1,nMax);
}
void Show(int a[],int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
int main()
{
int a[]={49,38,65,97,76,13,27};
Quicksort(a,0,dim(a)-1);
Show(a,dim(a));
system("pause");
return 0;
}
输出:
快速排序的变种算法:随机化快排、平衡快排、外部快排
快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的
结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入
数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为O(1/(2^n))。
-- 所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个
人一辈子的人品需求”。随机化快速排序的唯一缺点在于:一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个
相同的数排序,随机化快速排序的时间复杂度将毫无疑问提高到O(n^2).一种解决方法是进行扫描,使没有交换的情况下主元保留在原位置。
-- 平衡快排(Balanced QuickSort):每次尽可能地选择一个能够代表中值的元素作为关键数据,然后遵循普通快排的原则进行比较、替换和递归。通
常来说,选择这个数据的方法是取开头、结尾、中间3个数,通过比较选出其中的中值。取这3个值的好处是在实际问题中,出现近似顺序数据或逆序数
据的概率较大,此时中间数据必然成为中值,也是事实上的近似中值。万一遇到正好中间大两边小的数据,取的值都接近最值。由于至少能将两部分分开,
实际效率也有2倍左右的增加。
-- 外部快排(External QuickSort):与普通快排不同的是,关键数据是一段buffer,首先将之前和之后的M/2个元素读入buffer并对该buffer中的这些元素
进行排序,然后从被排序数组的开头(或结尾)读入下一个元素,加入这个元素小于buffer中的最小元素,把它写到最开头的空位上;假如这个元素大于
buffer中最大的元素,则写到最后的空位上,否则把buffer中最大或者最小的元素写入数组,并把这个元素放在buffer里,保持最大值高于这些关键数据,
最小值低于这些关键数据,从而避免对已经有序的中间的数据进行重排。完成后,数组的中间空位必然空出,把这个buffer写入数组中间空位。然后递归的
对外部更小的部分,循环的对其他部分进行排序。