快速排序是對冒泡排序的一種改進。
它的基本思想是:通過一次排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一不部分的所有數據都要小,然後再按次方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸或者非遞歸進行,以此達到整個數據變成有序序列。
快排實現方式:循環+遞歸
#include<cstdio>
#include<string.h>
#define ll long long
using namespace std;
int n;
ll arr[100010];
void swaps(int a, int b){
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
void qsort(int begin, int end){
if(begin>=end) return;
int l=begin, r=end; //邊界
int tmp=arr[begin]; //基準
do{
while(arr[l]<tmp) l++;
while(arr[r]>tmp) r--;
if(l<=r){
swaps(l, r);
l++;
r--;
}
}while(l<=r);
//分治
qsort(begin, r);
qsort(l, end);
}
int main(){
memset(arr, 0, sizeof(arr));
scanf("%d", &n);
for(int i=0; i<n; i++) scanf("%lld", &arr[i]);
qsort(0, n-1);
for(int i=0; i<n; i++) printf("%lld ", arr[i]);
return 0;
}
值得注意的是取基準值最好不要有序的取(例如從區間的開頭取或從結尾取)。
當數組順序是從大到小而快排要實現從小到大的排序,按順序取基準值會導致元素替換次數增多。
改良方案:爲了使每次基準都在合適的區間內,可以修改上述代碼中的基準爲
int tmp=arr[(begin+end)/2]; //基準
這樣一來既能使基準在begin~end的範圍內,又可以將取基準的順序打亂。
洛谷測試平臺
經典遞歸實現快排
(以區間第一個爲基準)
#include<cstdio>
#include<string.h>
#define ll long long
using namespace std;
int n;
ll arr[100010];
void swaps(int a, int b){
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
//只排一個元素
int p(int begin, int end){
if(begin>=end) return begin;
//每次都以區間第一個元素爲基準
if(arr[begin]>arr[begin+1]){
swaps(begin+1, begin);
return p(begin+1, end);
}
else if(arr[begin]==arr[begin+1]){
return p(begin+1, end);
}
else{
swaps(begin+1, end);
return p(begin, end-1);
}
}
void f(int begin, int end){
if(begin>=end){
return;
}
//a爲已排好序元素的下標
int a = p(begin, end);
//分治
f(begin, a-1);
f(a+1, end);
return;
}
int main(){
scanf("%d", &n);
memset(arr, 0, sizeof(arr));
for(int i=0; i<n; i++){
scanf("%lld", &arr[i]);
}
f(0, n-1);
for(int i=0; i<n; i++){
printf("%lld ", arr[i]);
}
return 0;
}
ps:
遞歸與循環那種方式更高效?
循環更高效,實現邏輯簡單相比於遞歸簡單。
遞歸調用函數中的變量的壓棧、出棧操作,如果遞歸的層次太多肯定會導致內存溢出、系統崩潰。例如:計算 n !,如果 n 太大了的話,就不能夠使用遞歸的方法來實現了。
遞歸好處:編寫代碼簡單,程序可讀性好。