快速排序过程
1)选择一个基准元素(key),通常选择第一个元素(low)或者最后一个元素(high),
2)通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
下面列举一个简单的例子,比如对如下数组 a 中的元素使用快速排序实现从小到大排序:
35 12 37 -58 54 76 22
1) 首先分别定义 low 和 high 用于存储数组第一个元素的下标和最后一个元素的下标,即 low=0,high=6。
2) 然后定义 key 用于存放基准数,理论上该基准数可以取序列中的任何一个数。此处就取数组的第一个元素,即把 a[low] 赋给 key。
3) 然后 key 和 a[high] 比较,即 35 和 22 比较,35>22,则它们互换位置:
22 12 37 -58 54 76 35
4) 然后 low++==1,key 和 a[low] 比较,即 35 和 12 比较,12<35,则不用互换位置;继续 low++==2,然后 key 和 a[low] 比较,即 35 和 37 比较,37>35,则它们互换位置:
22 12 35 -58 54 76 37
5) 然后 high--==5,key 和 a[high] 比较,即 35 和 76 比较,35<76,则不用互换位置;继续 high--==4,然后 key 和 a[high] 比较,即 35 和 54 比较,35<54,则不用互换位置;继续 high--==3,然后 key 和 a[high] 比较,即 35 和 -58 比较,35>–58,则它们互换位置:
22 12 -58 35 54 76 37
6) 然后 low++==3,此时 low==high,第一轮比较结束。从最后得到的序列可以看出,35 左边的都比 35 小,35 右边的都比 35 大。这样就以 35 为中心,把原序列分成了左右两个部分。接下来只需要分别对左右两个部分分别重复上述操作就行了。
#include <stdio.h>
#include <stdlib.h>
void Swap(int *,int *);
void Quicksort(int *,int ,int );
int main(){
int num,i;
char d = 'a';
int a[1001];
scanf("%d, ",&num);
for (i = 0;i<num;i++)
scanf("%d",&a[i]);
Quicksort(a,0,num-1);
for (i = 0;i<num;i++)
printf("%d ",a[i]);
return 0;
}
void Quicksort(int *a,int low,int high){
int key = a[low];
int l = low,h = high;
if (low>=high)
return;
while(low<high){
while(low<high && key<=a[high])
high--;
if(key>a[high]){
Swap(&a[low] , &a[high]);
low++;
}
while(low<high && key>=a[low])
low++;
if(key<a[low]){
Swap(&a[low] , &a[high]);
high--;}
}
Quicksort(a,l,low-1);
Quicksort(a,low+1,h);
}
void Swap(int *p,int *q){
int buf;
buf = *p;
*p = *q;
*q = buf;
return;
}
快速排序的改进
只对长度大于k的子序列递归调用快速排序,让原序列基本有序,然后再对整个基本有序序列用插入排序算法排序。实践证明,改进后的算法时间复杂度有所降低。
选择基准元的方式
对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列。
方法1 固定基准元
如果输入序列是随机的,处理时间是可以接受的。如果数组已经有序时,此时的分割就是一个非常不好的分割。
方法2 随机基准元
这是一种相对安全的策略。由于基准元的位置是随机的,那么产生的分割也不会总是会出现劣质的分割。在整个数组数字全相等时,仍然是最坏情况,时间复杂度是O(n^2)。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。
方法3 三数取中
引入的原因:虽然随机选取基准时,减少出现不好分割的几率,但是还是最坏情况下还是O(n^2),要缓解这种情况,就引入了三数取中选取基准。
分析:最佳的划分是将待排序的序列分成等长的子序列,最佳的状态我们可以使用序列的中间的值,也就是第N/2个数。可是,这很难算出来,并且会明显减慢快速排序的速度。这样的中值的估计可以通过随机选取三个元素并用它们的中值作为基准元而得到。事实上,随机性并没有多大的帮助,因此一般的做法是使用左端、右端和中心位置上的三个元素的中值作为基准元。