前言
本篇文章基本是從
常用排序算法總結(jié)(一)
快速排序
引申而來,其中大部分代碼和描述都來自這兩篇文章。
時間復(fù)雜度
在計算機(jī)科學(xué)中,算法的時間復(fù)雜度是一個函數(shù),它定量描述了該算法的運行時間。這是一個代表算法輸入值的字符串的長度的函數(shù)。時間復(fù)雜度常用大O符號表述,不包括這個函數(shù)的低階項和首項系數(shù)。使用這種方式時,時間復(fù)雜度可被稱為是漸近的,亦即考察輸入值大小趨近無窮時的情況。例如,如果一個算法對于任何大小為n的輸入,它至多需要 5n^3 + 3n 的時間運行完畢,那么它的漸近時間復(fù)雜度是 O(n^3)。
空間復(fù)雜度
時間復(fù)雜度是指在計算機(jī)科學(xué)與工程領(lǐng)域完成一個算法所需要的時間,是衡量一個算法優(yōu)劣的重要參數(shù)。時間復(fù)雜度越小,說明該算法效率越高,則該算法越有價值。
空間復(fù)雜度是指計算機(jī)科學(xué)領(lǐng)域完成一個算法所需要占用的存儲空間,一般是輸入?yún)?shù)的函數(shù)。它是算法優(yōu)劣的重要度量指標(biāo),一般來說,空間復(fù)雜度越小,算法越好。
列表
穩(wěn)定性
排序算法穩(wěn)定性的簡單形式化定義為:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai還在Aj之前,則稱這種排序算法是穩(wěn)定的。通俗地講就是保證排序前后兩個相等的數(shù)的相對順序不變。
對于不穩(wěn)定的排序算法,只要舉出一個實例,即可說明它的不穩(wěn)定性;而對于穩(wěn)定的排序算法,必須對算法進(jìn)行分析從而得到穩(wěn)定的特性。需要注意的是,排序算法是否為穩(wěn)定的是由具體算法決定的,不穩(wěn)定的算法在某種條件下可以變?yōu)榉€(wěn)定的算法,而穩(wěn)定的算法在某種條件下也可以變?yōu)椴环€(wěn)定的算法。
排序算法如果是穩(wěn)定的,那么從一個鍵上排序,然后再從另一個鍵上排序,第一個鍵排序的結(jié)果可以為第二個鍵排序所用。基數(shù)排序就是這樣,先按低位排序,逐次按高位排序,低位排序后元素的順序在高位也相同時是不會改變的。
簡單選擇排序
選擇排序也是一種簡單直觀的排序算法。它的工作原理很容易理解:初始時在序列中找到最小(大)元素,放到序列的起始位置作為已排序序列;然后,再從剩余未排序元素中繼續(xù)尋找最小(大)元素,放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
注意選擇排序與冒泡排序的區(qū)別:冒泡排序通過依次交換相鄰兩個順序不合法的元素位置,從而將當(dāng)前最小(大)元素放到合適的位置;而選擇排序每遍歷一次都記住了當(dāng)前最小(大)元素的位置,最后僅需一次交換操作即可將其放到合適的位置。
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- O(n^2)
// 最優(yōu)時間復(fù)雜度 ---- O(n^2)
// 平均時間復(fù)雜度 ---- O(n^2)
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 不穩(wěn)定
void SelectionSort(int A[], int n) {
for (int i = 0; i < n - 1; i++) { // i為已排序序列的末尾
int min = i;
for (int j = i + 1; j < n; j++) { // 未排序序列
if (A[j] < A[min]) { // 找出未排序序列中的最小值
min = j;
}
}
if (min != i){
Swap(A, min, i); // 放到已排序序列的末尾,該操作很有可能把穩(wěn)定性打亂,所以選擇排序是不穩(wěn)定的排序算法
}
}
}
選擇排序是不穩(wěn)定的排序算法,不穩(wěn)定發(fā)生在最小元素與A[i]交換的時刻。
比如序列:{ 5, 8, 5, 2, 9 },一次選擇的最小元素是2,然后把2和第一個5進(jìn)行交換,從而改變了兩個元素5的相對次序。
冒泡排序
重復(fù)地走訪過要排序的元素,依次比較相鄰兩個元素,如果他們的順序錯誤就把他們調(diào)換過來,直到?jīng)]有元素再需要交換,排序完成。這個算法的名字由來是因為越小(或越大)的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端。
冒泡排序算法的運作如下:
- 比較相鄰的元素,如果前一個比后一個大,就把它們兩個調(diào)換位置。
- 對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對。這步做完后,最后的元素會是最大的數(shù)。
- 針對所有的元素重復(fù)以上的步驟,除了最后一個。
- 持續(xù)每次對越來越少的元素重復(fù)上面的步驟,直到?jīng)]有任何一對數(shù)字需要比較。
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- O(n^2)
// 最優(yōu)時間復(fù)雜度 ---- 如果能在內(nèi)部循環(huán)第一次運行時,使用一個旗標(biāo)來表示有無需要交換的可能,可以把最優(yōu)時間復(fù)雜度降低到O(n)
// 平均時間復(fù)雜度 ---- O(n^2)
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 穩(wěn)定
void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
//設(shè)定一個標(biāo)記,若為true,則表示此次循環(huán)沒有進(jìn)行交換,也就是待排序列已經(jīng)有序,排序已然完成
boolean flag = true;。
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr,j,j+1);
flag = false;
}
}
if (flag) {
break;
}
}
}
最好的情況是已經(jīng)排好序,只需要n-1比較即可完成;最差的情況是倒序,需要n-1 + n-2 + n-3 + ...+ 2 + 1 = n(n-1)/2次比較,時間復(fù)雜度是O(n*n)。
這種寫法,冒泡排序是穩(wěn)定的,如果改成arr[j] >= arr[j + 1]
,那么就是不穩(wěn)定的。
雞尾酒排序(冒泡排序的優(yōu)化)
雞尾酒排序,也叫定向冒泡排序,是冒泡排序的一種改進(jìn)。此算法與冒泡排序的不同處在于從低到高然后從高到低,而冒泡排序則僅從低到高去比較序列里的每個元素。他可以得到比冒泡排序稍微好一點的效能。
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- O(n^2)
// 最優(yōu)時間復(fù)雜度 ---- 如果序列在一開始已經(jīng)大部分排序過的話,會接近O(n)
// 平均時間復(fù)雜度 ---- O(n^2)
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 穩(wěn)定
void CocktailSort(int A[]) {
int n = A.length;
int left = 0; // 初始化邊界
int right = n - 1;
while (left < right) {
for (int i = left; i < right; i++) { // 前半輪,將最大元素放到后面
if (A[i] > A[i + 1]) {
Swap(A, i, i + 1);
}
}
right--;
for (int i = right; i > left; i--) { // 后半輪,將最小元素放到前面
if (A[i - 1] > A[i]) {
Swap(A, i - 1, i);
}
}
left++;
}
}
以序列(2,3,4,5,1)為例,雞尾酒排序只需要訪問一次序列就可以完成排序,但如果使用冒泡排序則需要四次。但是在亂數(shù)序列的狀態(tài)下,雞尾酒排序與冒泡排序的效率都很差勁。
簡單插入排序
直接插入排序基本思想是每一步將一個待排序的記錄,插入到前面已經(jīng)排好序的有序序列中去,直到插完所有元素為止。
插入排序在實現(xiàn)上,通常采用in-place排序(即只需用到O(1)的額外空間的排序),因而在從后向前掃描過程中,需要反復(fù)把已排序元素逐步向后挪位,為最新元素提供插入空間。
具體算法描述如下:
- 從第一個元素開始,該元素可以認(rèn)為已經(jīng)被排序
- 取出下一個元素,在已經(jīng)排序的元素序列中從后向前掃描
- 如果該元素(已排序)大于新元素,將該元素移到下一位置
- 重復(fù)步驟3,直到找到已排序的元素小于或者等于新元素的位置
- 將新元素插入到該位置后
- 重復(fù)步驟2~5
// 分類 ------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- 最壞情況為輸入序列是降序排列的,此時時間復(fù)雜度O(n^2)
// 最優(yōu)時間復(fù)雜度 ---- 最好情況為輸入序列是升序排列的,此時時間復(fù)雜度O(n)
// 平均時間復(fù)雜度 ---- O(n^2)
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 穩(wěn)定
void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int j = i;
while (j > 0 && arr[j] < arr[j - 1]) {
swap(arr,j,j-1);
j--;
}
}
}
簡單插入排序在最好情況下,需要比較n-1次,無需交換元素,時間復(fù)雜度為O(n);在最壞情況下,時間復(fù)雜度依然為O(n2)。插入排序不適合對于數(shù)據(jù)量比較大的排序應(yīng)用。但是,如果需要排序的數(shù)據(jù)量很小,比如量級小于千,那么插入排序還是一個不錯的選擇。 插入排序在工業(yè)級庫中也有著廣泛的應(yīng)用,在STL的sort算法和stdlib的qsort算法中,都將插入排序作為快速排序的補(bǔ)充,用于少量元素的排序(通常為8個或以下)。
二分插入排序(插入排序優(yōu)化)
對于插入排序,如果比較操作的代價比交換操作大的話,可以采用二分查找法來減少比較操作的次數(shù),我們稱為二分插入排序。
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- O(n^2)
// 最優(yōu)時間復(fù)雜度 ---- O(nlogn)
// 平均時間復(fù)雜度 ---- O(n^2)
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 穩(wěn)定
void InsertionSortDichotomy(int A[]) {
int n = A.length;
for (int i = 1; i < n; i++) {
int target = A[i];
int left = 0;
int right = i - 1;
while (left <= right) { // 二分查找,找到新元素在排好序的序列中的位置
int mid = (left + right) / 2;
if (A[mid] > target)
right = mid - 1;
else
left = mid + 1;
}
for (int j = i - 1; j >= left; j--) { // 整體后移一位
A[j + 1] = A[j];
}
A[left] = target;
}
}
當(dāng)n較大時,二分插入排序的比較次數(shù)比直接插入排序的最差情況好得多,但比直接插入排序的最好情況要差,所當(dāng)以元素初始序列已經(jīng)接近升序時,直接插入排序比二分插入排序比較次數(shù)少。二分插入排序元素移動次數(shù)與直接插入排序相同,依賴于元素初始序列。
希爾排序
希爾排序,也叫遞減增量排序,是插入排序的一種更高效的改進(jìn)版本。希爾排序是不穩(wěn)定的排序算法。
希爾排序是基于插入排序的以下兩點性質(zhì)而提出改進(jìn)方法的:
- 插入排序在對幾乎已經(jīng)排好序的數(shù)據(jù)操作時,效率高,即可以達(dá)到線性排序的效率
- 插入排序一般來說是低效的,因為插入排序每次只能將數(shù)據(jù)移動一位
希爾排序通過將比較的全部元素分為幾個區(qū)域來提升插入排序的性能。這樣可以讓一個元素可以一次性地朝最終位置前進(jìn)一大步。然后算法再取越來越小的步長進(jìn)行排序,算法的最后一步就是普通的插入排序,但是到了這步,需排序的數(shù)據(jù)幾乎是已排好的了(此時插入排序較快)。
假設(shè)有一個很小的數(shù)據(jù)在一個已按升序排好序的數(shù)組的末端。如果用復(fù)雜度為O(n^2)的排序(冒泡排序或直接插入排序),可能會進(jìn)行n次的比較和交換才能將該數(shù)據(jù)移至正確位置。而希爾排序會用較大的步長移動數(shù)據(jù),所以小數(shù)據(jù)只需進(jìn)行少數(shù)比較和交換即可到正確位置。
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- 根據(jù)步長序列的不同而不同。已知最好的為O(n(logn)^2)
// 最優(yōu)時間復(fù)雜度 ---- O(n)
// 平均時間復(fù)雜度 ---- 根據(jù)步長序列的不同而不同。
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 不穩(wěn)定
void ShellSort(int A[]) {
int n = A.length;
int h = n/3;
while (h <= n) { // 生成初始增量
h = 3 * h + 1;
}
while (h >= 1){
for (int i = h; i < n; i++){
int j = i - h;
int target = A[i];
while (j >= 0 && A[j] > target){
A[j + h] = A[j];
j = j - h;
}
A[j + h] = target;
}
h = (h - 1) / 3; // 遞減增量
}
}
void ShellSort(int a[]) {
int n = a.length;
for (int gap = n/3; gap > 0; gap = gap/3) {
for(int j = gap; j < n; j++) {
for(int i = j - gap; i >= 0 && a[i] > a[i + gap]; i -= gap) {
swap(a, i, i + gap);
}
}
}
}
希爾排序是不穩(wěn)定的排序算法,雖然一次插入排序是穩(wěn)定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最后其穩(wěn)定性就會被打亂。
比如序列:{ 3, 5, 10, 8, 7, 2, 8, 1, 20, 6 },h=2時分成兩個子序列 { 3, 10, 7, 8, 20 } 和 { 5, 8, 2, 1, 6 } ,未排序之前第二個子序列中的8在前面,現(xiàn)在對兩個子序列進(jìn)行插入排序,得到 { 3, 7, 8, 10, 20 } 和 { 1, 2, 5, 6, 8 } ,即 { 3, 1, 7, 2, 8, 5, 10, 6, 20, 8 } ,兩個8的相對次序發(fā)生了改變。
歸并排序
歸并排序是創(chuàng)建在歸并操作上的一種有效的排序算法,效率為O(nlogn),1945年由馮·諾伊曼首次提出。
歸并排序的實現(xiàn)分為遞歸實現(xiàn)與非遞歸(迭代)實現(xiàn)。遞歸實現(xiàn)的歸并排序是算法設(shè)計中分治策略的典型應(yīng)用,我們將一個大問題分割成小問題分別解決,然后用所有小問題的答案來解決整個大問題。非遞歸(迭代)實現(xiàn)的歸并排序首先進(jìn)行是兩兩歸并,然后四四歸并,然后是八八歸并,一直下去直到歸并了整個數(shù)組。
歸并排序算法主要依賴歸并(Merge)操作。歸并操作指的是將兩個已經(jīng)排序的序列合并成一個序列的操作,歸并操作步驟如下:
- 申請空間,使其大小為兩個已經(jīng)排序序列之和,該空間用來存放合并后的序列
- 設(shè)定兩個指針,最初位置分別為兩個已經(jīng)排序序列的起始位置
- 比較兩個指針?biāo)赶虻脑兀x擇相對小的元素放入到合并空間,并移動指針到下一位置
- 重復(fù)步驟3直到某一指針到達(dá)序列尾
- 將另一序列剩下的所有元素直接復(fù)制到合并序列尾
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- O(nlogn)
// 最優(yōu)時間復(fù)雜度 ---- O(nlogn)
// 平均時間復(fù)雜度 ---- O(nlogn)
// 所需輔助空間 ------ O(n)
// 穩(wěn)定性 ------------ 穩(wěn)定
// 遞歸
void sort(int[] arr,int left,int right,int[] temp) {
if(left < right) {
int mid = (left+right)/2;
sort(arr,left,mid,temp);//左邊歸并排序,使得左子序列有序
sort(arr,mid+1,right,temp);//右邊歸并排序,使得右子序列有序
merge(arr,left,mid,right,temp);//將兩個有序子數(shù)組合并操作
}
}
// 非遞歸
void MergeSortIteration(int[] arr, int len, int[] temp) { // 非遞歸(迭代)實現(xiàn)的歸并排序(自底向上)
int left, mid, right;// 子數(shù)組索引,前一個為A[left...mid],后一個子數(shù)組為A[mid+1...right]
for (int i = 1; i < len; i *= 2) { // 子數(shù)組的大小i初始為1,每輪翻倍
left = 0;
while (left + i < len) { // 后一個子數(shù)組存在(需要歸并)
mid = left + i - 1;
right = mid + i < len ? mid + i : len - 1;// 后一個子數(shù)組大小可能不夠
merge(arr, left, mid, right, temp);
left = right + 1; // 前一個子數(shù)組索引向后移動
}
}
}
void merge(int[] arr,int left,int mid,int right,int[] temp){
int i = left;//左序列指針
int j = mid+1;//右序列指針
int t = 0;//臨時數(shù)組指針
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//將左邊剩余元素填充進(jìn)temp中
temp[t++] = arr[i++];
}
while(j<=right){//將右序列剩余元素填充進(jìn)temp中
temp[t++] = arr[j++];
}
t = 0;
//將temp中的元素全部拷貝到原數(shù)組中
while(left <= right){
arr[left++] = temp[t++];
}
}
}
歸并排序除了可以對數(shù)組進(jìn)行排序,還可以高效的求出數(shù)組小和(即單調(diào)和)以及數(shù)組中的逆序?qū)Γ斠?a href="http://www.lxweimin.com/p/3ab5033074f1" target="_blank">這篇博文。
堆排序
堆排序是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種選擇排序算法。堆排序是一種選擇排序,它的最壞,最好,平均時間復(fù)雜度均為O(nlogn),它也是不穩(wěn)定排序。堆是一種近似完全二叉樹的結(jié)構(gòu)(通常堆是通過一維數(shù)組來實現(xiàn)的),并滿足性質(zhì):以最大堆(也叫大根堆、大頂堆)為例,其中父結(jié)點的值總是大于它的孩子節(jié)點。這篇文章詳解
該數(shù)組從邏輯上講就是一個堆結(jié)構(gòu),我們用簡單的公式來描述一下堆的定義就是:
- 大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
- 小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:將待排序序列構(gòu)造成一個大頂堆,此時,整個序列的最大值就是堆頂?shù)母?jié)點。將其與末尾元素進(jìn)行交換,此時末尾就為最大值。然后將剩余n-1個元素重新構(gòu)造成一個堆,這樣會得到n個元素的次小值。如此反復(fù)執(zhí)行,便能得到一個有序序列了。
// 分類 -------------- 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) ---------- 數(shù)組
// 最差時間復(fù)雜度 ---- O(nlogn)
// 最優(yōu)時間復(fù)雜度 ---- O(nlogn)
// 平均時間復(fù)雜度 ---- O(nlogn)
// 所需輔助空間 ------ O(1)
// 穩(wěn)定性 ------------ 不穩(wěn)定
public void sort(int []arr){
//1.構(gòu)建大頂堆
for(int i=arr.length/2-1;i>=0;i--){
//從第一個非葉子結(jié)點從下至上,從右至左調(diào)整結(jié)構(gòu)
adjustHeap(arr,i,arr.length);
}
//2.調(diào)整堆結(jié)構(gòu)+交換堆頂元素與末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//將堆頂元素與末尾元素進(jìn)行交換
adjustHeap(arr,0,j);//重新對堆進(jìn)行調(diào)整
}
}
/**
* 調(diào)整大頂堆(僅是調(diào)整過程,建立在大頂堆已構(gòu)建的基礎(chǔ)上)
*/
public void adjustHeap(int []arr,int i,int length) {
int temp = arr[i];//先取出當(dāng)前元素i
for(int k=i*2+1;k<length;k=k*2+1) {//從i結(jié)點的左子結(jié)點開始,也就是2i+1處開始
if(k+1 < length && arr[k] < arr[k+1]) {//如果左子結(jié)點小于右子結(jié)點,k指向右子結(jié)點
k++;
}
if(arr[k] > temp){//如果子節(jié)點大于父節(jié)點,將子節(jié)點值賦給父節(jié)點(不用進(jìn)行交換)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//將temp值放到最終的位置
}
堆排序是一種選擇排序,整體主要由構(gòu)建初始堆+交換堆頂元素和末尾元素并重建堆兩部分組成。其中構(gòu)建初始堆經(jīng)推導(dǎo)復(fù)雜度為O(n),在交換并重建堆的過程中,需交換n-1次,而重建堆的過程中,根據(jù)完全二叉樹的性質(zhì),[log2(n-1),log2(n-2)...1]逐步遞減,近似為nlogn。所以堆排序時間復(fù)雜度一般認(rèn)為就是O(nlogn)級。
堆排序是不穩(wěn)定的排序算法,不穩(wěn)定發(fā)生在堆頂元素與A[i]交換的時刻。比如序列:{ 9, 5, 7, 5 },堆頂元素是9,堆排序下一步將9和第二個5進(jìn)行交換,得到序列 { 5, 5, 7, 9 },再進(jìn)行堆調(diào)整得到{ 7, 5, 5, 9 },重復(fù)之前的操作最后得到{ 5, 5, 7, 9 }從而改變了兩個5的相對次序。
快速排序
快速排序是由東尼·霍爾所發(fā)展的一種排序算法。在平均狀況下,排序n個元素要O(nlogn)次比較。在最壞狀況下則需要O(n^2)次比較,但這種狀況并不常見。事實上,快速排序通常明顯比其他O(nlogn)算法更快,因為它的內(nèi)部循環(huán)可以在大部分的架構(gòu)上很有效率地被實現(xiàn)出來。
快速排序使用分治策略(Divide and Conquer),每一次我們要取一個元素作為樞紐值,以這個數(shù)字來將序列劃分為兩部分。在此我們采用三數(shù)取中法,也就是取左端、中間、右端三個數(shù),然后進(jìn)行排序,將中間數(shù)作為樞紐值。
// 分類 ------------ 內(nèi)部比較排序
// 數(shù)據(jù)結(jié)構(gòu) --------- 數(shù)組
// 最差時間復(fù)雜度 ---- 每次選取的基準(zhǔn)都是最大(或最小)的元素,導(dǎo)致每次只劃分出了一個分區(qū),需要進(jìn)行n-1次劃分才能結(jié)束遞歸,時間復(fù)雜度為O(n^2)
// 最優(yōu)時間復(fù)雜度 ---- 每次選取的基準(zhǔn)都是中位數(shù),這樣每次都均勻的劃分出兩個分區(qū),只需要logn次劃分就能結(jié)束遞歸,時間復(fù)雜度為O(nlogn)
// 平均時間復(fù)雜度 ---- O(nlogn)
// 所需輔助空間 ------ 主要是遞歸造成的棧空間的使用(用來保存left和right等局部變量),取決于遞歸樹的深度,一般為O(logn),最差為O(n)
// 穩(wěn)定性 ---------- 不穩(wěn)定
public static void quickSort(int[] arr, int left, int right) {
if(left >= right) {
return;
}
int pivotPosition = partition(arr, left, right);
quickSort(arr, left, pivotPosition - 1);
quickSort(arr, pivotPosition + 1, right);
}
public static int partition(int[] arr, int left, int right) {
int pivot = arr[right];
int pivotPosition = left;
for(int i = left; i < right; i++) {
if(arr[i] < pivot) {
swap(arr, pivotPosition++, i);
}
}
swap(arr, pivotPosition, right);
return pivotPosition;
}
快速排序是不穩(wěn)定的排序算法,不穩(wěn)定發(fā)生在基準(zhǔn)元素與A[tail+1]交換的時刻。
比如序列:{ 1, 3, 4, 2, 8, 9, 8, 7, 5 },基準(zhǔn)元素是5,一次劃分操作后5要和第一個8進(jìn)行交換,從而改變了兩個元素8的相對次序。
終
關(guān)于算法,每一次看都有不同的感受,索性總結(jié)一遍。