一、快速排序
快速排序是冒泡排序的一种改进算法,相比于冒泡排序效率更优。
算法过程分析:
通过采用分治策略,围绕一个 x 将原始数组划分为两个子数组,使得前一个子数组的元素≤ x ≤ 后一个子数组元素,对两个子数组进行递归排序,再合并成一个有序数组。
1.选取一个基准元素 key(通常默认为数组的最左端),通过 key 将数组分为左右两端,使得左端数组全部 ≤ 基准元素 key ,右端数组全部元素 ≥ 基准元素key。
2.两端数组可以独立排序。对于左端数组,又可以取一个基准元素,将该端数组元素分成左右两部分,使得左端数组全部 ≤ 基准元素 key ,右端数组全部元素 ≥ 基准元素key。右侧数组做类似处理。
3.通过递归重复上述操作,当全部递归结束时,原数组也排序完成。
细节分析:
步骤1:将数组如何分成两个子数组,使得左端数组全部 ≤ 基准元素 key ,右端数组全部元素 ≥ 基准元素key
①j从右向左,找到小于key的数组元素
②i从左向右,找到大于key的数组元素,将二者交换。
③上述操作直至 i==j 时结束
④此时再将基准值与 a[i](或a[j],i==j)交换,就使得两个子数组上述条件成立。
int i=left,j=right;
while(i!=j)
{while(a[j]>=temp && i<j) j--;while(a[i]<=temp && i<j) i++;swap(a[i],a[j]);
}
swap(a[left],a[i]);
步骤2:
分治策略,将左右两个子数组进行相同的操作。
quick_sort(left,i-1);
quick_sort(i+1,right);
重复操作,通过递归分别进入两个子数组操作。
当全部返回时,结束递归,数组完成排序。
思考:为什么 i 从左往右移动,j从右往左移动时,j 先移动?
因为当 i 先移动时,i 一定会往右至少移动一次,出现以下情况:
① i 不停移动,直至移动到数组右端,此时交换i,j,出现错误,如图。
② i 先移动,移动到中间某个点时,j再移动,移动到i,j相等时交换,出现错误。
相反,如果是 j 先移动, 都能使得正确交换,必须先找到小于基准值key的元素。
二、快速排序完整代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int a[10001];
int n;
void swap(int a1,int b1) {int w=a[a1];a[a1]=a[b1];a[b1]=w;} //交换函数
void qsort1(int begin,int end) // 快排的实现
{if(begin > end) return ; // 退出快排函数 避免出现死循环int tem=a[begin]; // 记录基准点int i=begin,j=end;while(i!=j){while(a[j]>=tem && i<j) j--; //从右边开始找小于基准点的数while(a[i]<=tem && i<j) i++; //从左边开始找大于基准点的数if(i<j) swap(i,j); }a[begin]=a[i]; // 交换基准点和第i个 确保基准点左边全部比其小 右边全部不它大a[i]=tem;qsort1(begin,i-1); // 继续分成两个部分进行快速排序qsort1(i+1,end);}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);qsort1(1,n);for(int i=1;i<=n;i++) printf("%d ",a[i]);// system("pause");return 0;
}
三、算法的性能分析
时间复杂度:
理想情况:每次都尽可能将左端数组和右端数组等分递归,这样通过计算得出
时间复杂度与归并排序相同,为
最差情况:不难发现,当排序数组为顺序或者逆序时,每次 i,j 的移动都是从左到右
时间计算 会与冒泡排序相同 复杂度为
一般情况下,快速排序的时间复杂度为 ,但是不稳定。
洛谷—排序P1177 这道题,数据仍然卡快速排序,时间复杂度来到 ,需要对其进行优化。
四、快速排序优化
上述快速排序总是将基准值默认为数组中第一个元素,当数组成顺序或者逆序时,出现最慢的情况。
优化方法:在排序数组中随机选择一个数作为基准数进行排序。
基准数随机的结果:
①运行时间与输入数据的次序无关
②无特定输入数据匹配最差情况
③最差情况仅仅由随机数生成器所决定
对随机化快速排序的时间复杂度: (不稳定)
四、sort() 函数
在C++中使用sort()函数需要使用#include<algorithm>
头文件。algorithm意为"算法",是C++的标准模版库(STL)中最重要的头文件之一,提供了大量基于迭代器的非成员模版函数。
可以简单使用 sort() 函数对数组进行排序
#include<stdio.h>
#include<string.h>
#include<algorithm>
int main()
{int n,a[10001];scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+1+n); // 使数组从 a[1] 到 a[n] 排序for(int i=1;i<=n;i++) printf("%d ",a[i]);return 0;
}