三種快速排序以及快速排序的優化

2022-08-25 18:09:32 字數 4639 閱讀 5986

1、快速排序的基本思想:

快速排序使用分治的思想,通過一趟排序將待排序列分割成兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小。之後分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。

2、快速排序的三個步驟:

(1)選擇基準:在待排序列中,按照某種方式挑出乙個元素,作為 "基準"(pivot)

(2)分割操作:以該基準在序列中的實際位置,把序列分成兩個子串行。此時,在基準左邊的元素都比該基準小,在基準右邊的元素都比基準大

(3)遞迴地對兩個序列進行快速排序,直到序列為空或者只有乙個元素。

3、選擇基準的方式

對於分治演算法,當每次劃分時,演算法若都能分成兩個等長的子串行時,那麼分治演算法效率會達到最大。也就是說,基準的選擇是很重要的。選擇基準的方式決定了兩個分割後兩個子串行的長度,進而對整個演算法的效率產生決定性影響。

最理想的方法是,選擇的基準恰好能把待排序序列分成兩個等長的子串行

我們介紹三種選擇基準的方法

方法(1):固定位置

如果輸入序列是隨機的,處理時間可以接受的。如果陣列已經有序時,此時的分割就是乙個非常不好的分割。因為每次劃分只能使待排序序列減一,快速排序淪為起泡排序,時間複雜度為θ(n^2),此時為最壞情況。

方法(2):隨機選取基準

引入的原因:在待排序列是部分有序時,固定選取樞軸使快排效率底下,要緩解這種情況,就引入了隨機選取樞軸

思想:取待排序列中任意乙個元素作為基準

/*隨機選擇樞軸的位置,區間在low和high之間*/  

int selectpivotrandom(int arr,int low,int high)

由於樞軸的位置是隨機的,那麼產生的分割也不會總是會出現劣質的分割。在整個陣列數字全相等時,仍然是最壞情況,時間複雜度是o(n^2)。實際上,隨機化快速排序得到理論最壞情況的可能性僅為1/(2^n)。所以隨機化快速排序可以對於絕大多數輸入資料達到o(nlogn)的期望時間複雜度。

方法(3):三數取中(median-of-three)

引入的原因:雖然隨機選取樞軸時,減少出現不好分割的機率,但是還是最壞情況下還是o(n^2),要緩解這種情況,就引入了三數取中選取樞軸

最佳的劃分是將待排序的序列分成等長的子串行,最佳的狀態我們可以使用序列的中間的值,也就是第n/2個數。可是,這很難算出來,並且會明顯減慢快速排序的速度。這樣的中值的估計可以通過隨機選取三個元素並用它們的中值作為樞紐元而得到。事實上,隨機性並沒有多大的幫助,因此一般的做法是使用左端、右端和中心位置上的三個元素的中值作為樞紐元。顯然使用三數中值分割法消除了預排序輸入的不好情形,並且減少快排大約14%的比較次數

舉例:待排序序列為:8 1 4 9 6 3 5 2 7 0

左邊為:8,右邊為0,中間為6.

我們這裡取三個數排序後,中間那個數作為樞軸,則樞軸為6

注意:在選取中軸值時,可以從由左中右三個中選取擴大到五個元素中或者更多元素中選取,一般的,會有(2t+1)平均分割槽法(median-of-(2t+1),三平均分割槽法英文為median-of-three)。

具體思想:對待排序序列中low、mid、high三個位置上資料進行排序,取他們中間的那個資料作為樞軸,並用0下標元素儲存樞軸。

即:採用三數取中,並用0下標元素儲存樞軸。

/*函式作用:取待排序序列中low、mid、high三個位置上資料,選取他們中間的那個資料作為樞軸*/  

int selectpivotmedianofthree(int arr,int low,int high)

if (arr[low] > arr[high])//目標: arr[low] <= arr[high]

if (arr[mid] > arr[low]) //目標: arr[low] >= arr[mid]

//此時,arr[mid] <= arr[low] <= arr[high]

return arr[low];

//low的位置上儲存這三個位置中間的值

//分割時可以直接使用low位置的元素作為樞軸,而不用改變分割函式了

}

測試資料:

測試資料分析:使用三數取中選擇樞軸優勢還是很明顯的,但是還是處理不了重複陣列

其他優化:

優化1.當待排序序列的長度分割到一定大小後(當元素比較少的時候),用插入排序

原因:對於很小和部分有序的陣列,快排不如插排好。當待排序序列的長度分割到一定大小後,繼續分割的效率比插入排序要差,此時可以使用插排而不是快排

截止範圍:待排序序列長度n = 10,雖然在5~20之間任一截止範圍都有可能產生類似的結果,這種做法也避免了一些有害的退化情形。

if (high - low + 1 < 10)  

//else時,正常執行快排

針對隨機數組,使用三數取中選擇樞軸+插排,效率還是可以提高一點,真是針對已排序的陣列,是沒有任何用處的。因為待排序序列是已經有序的,那麼每次劃分只能使待排序序列減一。此時,插排是發揮不了作用的。所以這裡看不到時間的減少。另外,三數取中選擇樞軸+插排還是不能處理重複陣列

優化2、在一次分割結束後,可以把與key相等的元素聚在一起,繼續下次分割時,不用再對與key相等元素分割

舉例:待排序序列 1 4 6 7 6 6 7 6 8 6

三數取中選取樞軸:下標為4的數6

轉換後,待分割序列:6 4 6 7 1 6 7 6 8 6

樞軸key:6

本次劃分後,未對與key元素相等處理的結果:1 4 6 6 7 6 7 6 8 6

下次的兩個子串行為:1 4 6 和 7 6 7 6 8 6

本次劃分後,對與key元素相等處理的結果:1 4 6 6 6 6 6 7 8 7

下次的兩個子串行為:1 4 和 7 8 7

經過對比,我們可以看出,在一次劃分後,把與key相等的元素聚在一起,能減少迭代次數,效率會提高不少

具體過程:在處理過程中,會有兩個步驟

第一步,在劃分過程中,把與key相等元素放入陣列的兩端

第二步,劃分結束後,把與key相等的元素移到樞軸周圍

舉例:待排序序列 1 4 6 7 6 6 7 6 8 6

三數取中選取樞軸:下標為4的數6

轉換後,待分割序列:6 4 6 7 1 6 7 6 8 6

樞軸key:6

第一步,在劃分過程中,把與key相等元素放入陣列的兩端

結果為:6 4 1 6(樞軸) 7 8 7 6 6 6

此時,與6相等的元素全放入在兩端了

第二步,劃分結束後,把與key相等的元素移到樞軸周圍

結果為:1 4 66(樞軸)  6 6 6 7 8 7

此時,與6相等的元素全移到樞軸周圍了

之後,在1 4 和 7 8 7兩個子串行進行快排

void qsort(int arr,int low,int high)  

//一次分割

int key = selectpivotmedianofthree(arr,low,high);//使用三數取中法選擇樞軸

while(low < high)

high--;

} arr[low] = arr[high];

while(high > low && arr[low] <= key)

low++;

} arr[high] = arr[low];

} arr[low] = key;

//一次快排結束

//把與樞軸key相同的元素移到樞軸最終位置周圍

int i = low - 1;

int j = first;

while(j < left && arr[i] != key)

i = low + 1;

j = last;

while(j > right && arr[i] != key)

qsort(arr,first,low - 1 - leftlen);

qsort(arr,low + 1 + rightlen,last);

}

測試資料分析:三數取中選擇樞軸+插排+聚集相等元素的組合,效果竟然好的出奇。

原因:在陣列中,如果有相等的元素,那麼就可以減少不少冗餘的劃分。這點在重複陣列中體現特別明顯啊。

其實這裡,插排的作用還是不怎麼大的。

優化3:優化遞迴操作(迴圈加棧,減少異常)

快排函式在函式尾部有兩次遞迴操作,我們可以對其使用尾遞迴優化

優點:如果待排序的序列劃分極端不平衡,遞迴的深度將趨近於n,而棧的大小是很有限的,每次遞迴呼叫都會耗費一定的棧空間,函式的引數越多,每次遞迴耗費的空間也越多。優化後,可以縮減堆疊深度,由原來的o(n)縮減為o(logn),將會提高效能。

void qsort(int arr,int low,int high)  

while(low < high)

}

注意:在第一次遞迴後,low就沒用了,此時第二次遞迴可以使用迴圈代替

概括:這裡效率最好的快排組合 是:三數取中+插排+聚集相等元素,它和stl中的sort函式效率差不多

優化4:使用並行或多執行緒處理子串行(略)

本文**於:

三種快速排序

3種快排的實現方式,1是不適合重複元素多,2可以適應重複元素多,3可以優化重複元素多的時間 author hxj 2020年7月27日 public class main int l 0,r arr.length 1 quicksort arr,l,r private static void qui...

三種快速排序法

交換函式 為了提高效率,當所交換的兩個元素值不相等時,用異或運算 void swap int a,int b else 第一種快排 只有乙個長度n,每次需計算出low和high指標 int quicksort process1 int a,int n a high temp return high ...

三種改良排序(希爾排序,堆排序,快速排序)

一 改良的直接插入排序 希爾排序 優化考慮 因為直接插入排序對於大致有順序的數列效率較高,希爾排序即將數列先進行一定排序 希爾 for step count 2 step step 2 一次 for i start step i count i step for t i t j t step dat...