快速排序是冒泡排序的升級版,都是通過不斷交換數據來進行排序。快速排序的基本思想是:選出一個關鍵字,通過一次遍歷,把比關鍵字小的數據都放到關鍵字左邊,把比關鍵字大的數據都放到關鍵字的右邊。然後在對左右兩邊依次進行快速排序。
由此可以看出,第一步關鍵字的選擇很大程度上決定了快速排序的性能,如果關鍵字剛好選到了在中間左右的數據,那麼經過一次排序後會把整段數據分成兩段基本相等的數據,在進行下去效率就比較高;若果一不小心選擇了最大或者最小的數據,那麼一次排序下來相當於只排好了一個數據,相當不划算。爲了提高性能,通常使用三數取中的方法來選取關鍵字,由於數據是隨機排列的,可以分別在數組的左端、右端、和中間各取一個數,然後把最大的和最小的去掉,留下中間的值作爲關鍵字,三個數同時都取得較大或者較小的概率比較小,因此可以接受。在部分情況下,還會用到九數取中的方法,就是分別取三組三個數,把三組的中數再作比較留下中間的值。
快速排序是一種不穩定的排序,其時間複雜度爲O(nlogn),空間複雜度爲O(logn)~O(n)。
//快速排序函數
//L[]爲待排序序列,low爲最低位的下標,high爲最高位的下標
void QuickSort(int L[], int low, int high)
{
int middle = 0; //middle爲關鍵字返回的下標
while (low < high) {
middle = Partition(L,low,high); //把關鍵字排好位置返回其下標
QuickSort(L, low, middle - 1); //對關鍵字左邊的部分進行遞歸快速排序
low = middle + 1; //對關鍵字右邊的部分進行遞歸快速排序
}
}
//將關鍵字排好順序,這是快速排序中最重要的步驟
//L[]爲待排序序列,low爲最低位的下標,high爲最高位的下標
int Partition(int L[],int low, int high)
{
int middlekey = 0;
//首先通過三數取中算法得到比較中值得關鍵字
int m = low + (high - low) / 2; //計算數組中間元素下標
if(L[low] > L[high])
{
swap(L, low, high); //保證高位得數比低位的大
}
if(L[m] > L[high])
{
swap(L, m, high); //保證高位得數比中間的的大
}
if (L[low] < L[m])
{
swap(L, low, m); //把三個數的中間值放在L[low]的位置上
}
middlekey = L[low];
L[0] = middlekey; //把選出來的中值備份到L[0]的位置上
while (low < high && L[high] >= middlekey) //當後面的值一直比中值打,則不用交換,high向前移動
{
high--;
}
//如果跳出while循環則代表高位的值比中值小,把高位的值覆蓋到低位
L[low] = L[high]; //此函數返回的是中值的下標,因此數據替換掉沒有關係
//對低位的處理和高位一樣
while (low < high && L[low] <= middlekey)
{
low++;
}
L[high] = L[low];
L[low] = L[0];
return low;
}
快速排序的時間複雜度:
最壞的情況:所選的關鍵字每次都是最大值或者最小值,這樣的話快速排序就成了冒泡排序,時間複雜度爲O(n^2)
最優/平均情況:第一次調用partition將整個數組掃描一遍,做n次比較。遞歸logn次,所以時間複雜度爲O(nlogn)
空間複雜度:
最壞情況:進行n - 1次遞歸調用,其空間複雜度爲O(n)
平均情況:O(logn)