快速排序(標準版)
思想:
分治思想
算法思路:
- 確定分界點 q[l],q[(l+r)/2],q[r],隨機
- 調整區間:確定一個分界點x,使得所有左邊的數都小於等於x,所有右邊的數都大於等於x
- 遞歸處理左右兩段內容
調整區間方法一:(暴力的調整區間,時間複雜度O(n))
-
創建兩個數組a[],b[]
-
q[l~r]:
- q[i]<=x x->a[]
- q[i]>x x->b[]
-
a[]->q[] , b[] -> q[]
調整區間方法二:
- 使用兩個指針,指向兩端,同時向x的方向走
- 左指針指到一個大於x的數後停止,右指針指到一個小於x的數後停止
- 兩個數字交換
- 左右指針都走到x時,確保左邊的數字都小於等於x,右邊的數字都大於等於x
例題:
題目描述:
給定你一個長度爲n的整數數列。
請你使用快速排序對這個數列按照從小到大進行排序
並將排好序的數列按順序輸出。
輸入格式
輸入共兩行,第一行包含整數 n。
第二行包含 n 個整數(所有整數均在1~109109範圍內),表示整個數列。
輸出格式
輸出共一行,包含 n 個整數,表示排好序的數列。
數據範圍
1≤n≤100000
輸入樣例:
5
3 1 2 4 5
輸出樣例:
1 2 3 4 5
代碼1:
//快速排序
#include<iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];
void quick_sort(int q[],int l,int r){
if(l>=r)return ;
int x = q[l],i=l-1,j=r+1;
while(i<j){
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j) q[i]^=q[j],q[j]^=q[i],q[i]^=q[j];
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i=0;i<n;i++)printf("%d ",q[i]);
return 0;
}
注意:如果選取的x爲端點值,可能會超時
原因:當給定的序列有序時,如果每次選擇區間左端點進行劃分,每次會將區間[L, R]劃分成[L, L]和[L + 1, R],那麼相當於每次遞歸右半部分的區間長度只會減少1,所以就需要遞歸 n−1次了,時間複雜度會達到 n2。但每次選擇區間中點或者隨機值時,劃分的兩個子區間長度會比較均勻,那麼期望只會遞歸 logn層。
解決辦法:**x由端點值修改爲中間值
AC代碼
#include<iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];
void quick_sort(int q[],int l,int r){
if(l>=r)return ;
int x = q[(l+r)/2],i=l-1,j=r+1;
while(i<j){
do i++;while(q[i]<x);
do j--;while(q[j]>x);
if(i<j) q[i]^=q[j],q[j]^=q[i],q[i]^=q[j];
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&q[i]);
quick_sort(q,0,n-1);
for(int i=0;i<n;i++)printf("%d ",q[i]);
return 0;
}