快排的基础概念和优化
快排的基础概念:
- 选用一个基本值key,将比key小的放入左边,比key大的放入右边,不断重复,最终得到有序数列。
- 快排是一个比较高效的排序方法,时间复杂度是 n*log2n 。
- 快排是有一定的不稳定性,当数列倒序、相同元素较多时,快排比较其它排序方法没有更优越性
快排的实现过程(如图所示):
基本快排的实现:
代码如下:
void quickSort(int *a,int left,int right){
if(left >= right) return;
int pos=a[left];
int low=left,row=right;
while(low<row){
while(a[row]>=pos && low<row)
row--;
a[low]=a[row];
while(a[low]<=pos && low<row)
low++;
a[row]=a[low];
}
a[low]=pos;
quickSort(a,left,low-1);
quickSort(a,low+1,right);
}
快排的优化:
- 基础的快排是默认区间的左边第一个元素为key值,但数列倒序时,并没有起到优化排序的作用,所以要随机取key值;
- 当区间长度小于10时,可以用插排,插排和快排此时时间复杂度差不多,但是栈是有限的,使用插排可以减少栈深;
- 提高相等元素的聚集度也是可以减少算法的时间复杂度的。
三数取中法(代码如下):
//三数取中法 (选取轴值)
void selectQSort(int *a,int left,int right){
int mid=left + ((right-left)>>1); //选取中间值
if(a[mid]<a[left]) swap(a[mid],a[left]);
if(a[left]>a[right]) swap(a[left],a[right]);
if(a[right]<a[mid]) swap(a[right],a[mid]);
Qswap(a[mid],a[left]); //返回第二大的值
}
插排(代码如下):
//插排 (折半插入法)
void InsertSort(int *a,int left,int right){
int i,j,temp,low,row,mid;
for(i=left+1;i<=right;i++){
temp=a[i];
low=left,row=i-1;
while(low<=row){
mid=low+((row-low)>>1);
if(a[mid] <= temp){
low=mid+1;
}else{
row=mid-1;
}
}
for(j=i-1;j > row;j--) a[j+1]=a[j];
a[row+1]=temp;
}
}
相等元素聚集(代码如下):
void quickSort(int *a,int left,int right){
if(left>=right) return;
//当 right-left < 10 时,快排和插排相当(减少调用栈数)
if(right-left < 10) {
InsertSort(a,left,right);
return;
}
if(left<right){
selectQSort(a,left,right);
int tpr=right,tpl=left;
int pos=a[left];
int low=left,row=right;
while(low<row){
while(a[row]>=pos && low<row) {
if(a[row]==pos){ //将相等元素聚集数轴的右方
swap(a[row],a[tpr]);
tpr--;
}
row--;
}
a[low]=a[row];
while(a[low]<=pos && low<row) {
//由于是以a[left]为轴值,所以要有 low!=left ,确保元素的唯一性
if(a[low]==pos && low!=left){ //将相等元素聚集在数轴的左方
swap(a[low],a[tpl]);
tpl++;
}
low++;
}
a[row]=a[low];
}
a[low]=pos;
//分别将右方、左方的相等元素聚集在 pos 旁边
if(tpr==tpl) return;//表示该区间的所有元素相等
if(tpr<right){
while(tpr<=right && row<tpr){
tpr++;
row++;
if(tpr<=right) swap(a[row],a[tpr]);
}
row--;
}
if(tpl>left){
while(tpl>=left && low>tpl){
tpl--;
low--;
if(tpl>=left) swap(a[low],a[tpl]);
}
low++;
}
quickSort(a,left,low-1);
quickSort(a,row+1,right);
}
}
这是完整的代码:
#include<iostream>
using namespace std;
int n;
//交换函数
void Qswap(int &a,int &b){
int tp=a;
a=b;b=tp;
}
//三数取中法 (选取轴值)
void selectQSort(int *a,int left,int right){
int mid=left + ((right-left)>>1); //选取中间值
if(a[mid]<a[left]) Qswap(a[mid],a[left]);
if(a[left]>a[right]) Qswap(a[left],a[right]);
if(a[right]<a[mid]) Qswap(a[right],a[mid]);
Qswap(a[mid],a[left]); //返回第二大的值
}
//插排 (折半插入法)
void InsertSort(int *a,int left,int right){
int i,j,temp,low,row,mid;
for(i=left+1;i<=right;i++){
temp=a[i];
low=left,row=i-1;
while(low<=row){
mid=low+((row-low)>>1);
if(a[mid] <= temp){
low=mid+1;
}else{
row=mid-1;
}
}
for(j=i-1;j > row;j--) a[j+1]=a[j];
a[row+1]=temp;
}
}
//快排
void quickSort(int *a,int left,int right){
if(left>=right) return;
//当 right-left < 10 时,快排和插排相当(减少调用栈数)
if(right-left < 10) {
InsertSort(a,left,right);
return;
}
if(left<right){
selectQSort(a,left,right);
int tpr=right,tpl=left;
int pos=a[left];
int low=left,row=right;
while(low<row){
while(a[row]>=pos && low<row) {
if(a[row]==pos){ //将相等元素聚集数轴的右方
Qswap(a[row],a[tpr]);
tpr--;
}
row--;
}
a[low]=a[row];
while(a[low]<=pos && low<row) {
//由于是以a[left]为轴值,所以要有 low!=left ,确保元素的唯一性
if(a[low]==pos && low!=left){ //将相等元素聚集在数轴的左方
Qswap(a[low],a[tpl]);
tpl++;
}
low++;
}
a[row]=a[low];
}
a[low]=pos;
//分别将右方、左方的相等元素聚集在 pos 旁边
if(tpr==tpl) return;//表示该区间的所有元素相等
if(tpr<right){
while(tpr<=right && row<tpr){
tpr++;
row++;
if(tpr<=right) Qswap(a[row],a[tpr]);
}
row--;
}
if(tpl>left){
while(tpl>=left && low>tpl){
tpl--;
low--;
if(tpl>=left) Qswap(a[low],a[tpl]);
}
low++;
}
quickSort(a,left,low-1);
quickSort(a,row+1,right);
}
}
/*
样例:
8
2 4 3 6 1 7 5 8
10
4 2 4 3 6 5 6 6 8 9
*/
int main(){
int i;
cin>>n;
int a[n];
for(i=0;i<n;i++) cin>>a[i];
quickSort(a,0,n-1);
for(i=0;i<n-1;i++) cout<<a[i]<<" ";
cout<<a[n-1]<<endl;
}
最后,总结一下,快排的优化是:三数取中+插排+聚集相等元素,其实还有尾插等,另外本贴如果有什么错误之处,望贴友指出。