目錄
快速排序基本原理
快速排序兩種演算法
非遞迴實現快速排序 總結
(本文章僅作為記錄及總結)
簡單的說,快速排序(這裡是公升序)就是:
①確定乙個樞紐元素pivot;如圖示例:②將序列中所有的元素和樞紐元素pivot進行比較,比它大的放到右邊,比它小的放到左邊;
③對左右兩個區間的元素,重新進行①②兩個操作。
這裡兩個問題比較關鍵:如何選擇樞紐元素?如何進行進行第②步?
樞紐元素選擇不當,會導致排序效率低下,假如每次選擇的樞紐元素是子串行中的最大值,那麼在進行第②步操作時,會將所有元素都放到該元素的左邊,該值右邊為空,然後下次拆區間時,右邊區間啥都沒有,左邊區間只排出了剛選擇的樞紐值,時間複雜度退化成
第②步中,有兩種通常的方法來進行計算:挖坑填數法和指標交換法,這裡分別進行了介紹,同時還介紹了一種非遞迴的方式的實現。
顧名思義,用一種先挖坑,再填數的方式,實現基於樞紐元素的左右拆分。
如圖,這裡以乙個迴圈為例進行分解講解:
a.初始狀態如下,序列為value陣列,我們選擇樞紐pivot為序列頭部元素,在這裡挖個坑,同時將樞紐元素提前儲存起來,坑的位置是可以用來交換其他元素使用的。(樞紐元素可以選擇其他數值,可以通過交換到首部來保證演算法的統一性)。i為頭部,j為尾部。
b.先從右往左如果value[j] < pivot,將該數值value[j]填坑到樞紐(此時為i)處,此時i位置被占用,i++右移,j處變為坑,然後跳出b步驟。如圖:
如果value[j] > pivot,j左移j--,繼續進行b步驟,直到j==i或value[j] < pivot。
c.從左往右如果value[i] > pivot,將該數值value[i]填坑到上個坑(j)處,此時j被占用,j--,i處變為坑,跳出該步驟。否則i++,繼續進行c步驟,直到i==j或value[i] > pivot。
對於上圖,i所在位置1
此時value[i]為10,大於pivot(7),則j處設坑,將i賦值到j原始坑處,同時j--,變為:
d.持續進行b和c兩個步驟,最後的圖為:此時i不滿足je.最後將pivot的原始值7賦值到j處:
f.遞迴剩餘的兩個子串行(除了樞紐元素)最終的**(c語言描述):
void quick_sort1(int value, int startindex, int endindex)
int pivotvalue = value[startindex];//坑的原始值(樞紐元素),取第乙個元素
int i = startindex;//此時坑為i
int j = endindex;
while (i < j)
j--;
}//從左往右
while (i < j)
i++;}}
assert(i == j);
value[i] = pivotvalue;
//遞迴其他的
quick_sort1(value, startindex, i-1);
quick_sort1(value, i+1, endindex);
}
此方法和上面類似,只是沒有【挖坑】的概念,只要滿足一定條件後,交換兩個指標(i和j)即可。
這裡樞紐元素還是選擇首部,交換的條件是從右往左找到①確定樞紐元素
②從右往左,對j進行操作(j--),一直找到value[j] < pivot停止;
③從左往右,對i進行操作(i++),一直找到value[i] > pivot停止;
④交換i和j處的數值,繼續②和③的搜尋;
⑤最後j==j,交換樞紐元素和i的數值即可;
⑥遞迴左右子串行,從①開始。
具體**如下:
void quick_sort2(int value, int startindex, int endindex)
//選左第乙個元素為樞紐
int pivotvalue = value[startindex];
int i = startindex;
int j = endindex;
while (i < j)
j--;
}while (i < j)
i++;
}//交換i和j
std::swap(value[i], value[j]);
}assert(i == j);
std::swap(value[startindex], value[i]);
quick_sort1(value, startindex, i-1);
quick_sort1(value, i+1, endindex);
}
這裡基於挖坑法,自定義乙個棧來儲存遞迴時傳遞的引數,初始時把大的邊界傳入棧,然後開始迴圈棧,迴圈棧中的步驟和上面的一樣,最後拆分子序列遞迴修改為壓入棧兩個引數。
**如下:
typedef struct tstackvalue
tstackvalue;
//非遞迴方式,採用挖坑填數法
void quick_sort1_norecursive(int value, int startindex, int endindex)
tstackvalue tvalue = ;
stackvalue.push(tvalue);
while (!stackvalue.empty())
//下面和挖坑法基本一樣
int i = topvalue.start;
int j = topvalue.end;
int pivotvalue = value[i];
while (i < j)
i++;}}
value[i] = pivotvalue;
tstackvalue tvalue1 = ;//建立兩個棧元素來代替遞迴
tstackvalue tvalue2 = ;
stackvalue.pop();
stackvalue.push(tvalue1);
stackvalue.push(tvalue2);}}
理解了左右元素的交換原理,注意大於小於等於的邊界,很容易就可以把**寫出來,一定要注意樞紐元素的選擇。在實際應用中當元素個數少時,會有其他的演算法來減少遞迴次數,實際的排序演算法是多種方式的混搭。
遞迴 快速排序 快速排序
問題描述 用遞迴來實現快速排序 quick sort 演算法。快速排序演算法的基本思路是 假設要對乙個陣列a進行排序,且a 0 x。首先對陣列中的元素進行調整,使x放在正確的位置上。同時,所有比x小的數都位於它的左邊,所有比x大的數都位於它的右邊。然後對於左 右兩段區域,遞迴地呼叫快速排序演算法來進...
排序 快速排序
快速排序時實踐中最快的一直排序,平均時間是0 nlogn 最壞的情況是o n2 但是很容易將這種情況避免 空間複雜度 o n lgn 不穩定。快速排序時基於分治模式處理的,對乙個典型子陣列a p.r 排序的分治過程為三個步驟 1.分解 a p.r 被劃分為倆個 可能空 的子陣列a p q 1 和a ...
排序 快速排序
定義 在快速排序演算法中,使用了分治策略,將要排序的序列分成兩個子串行,然後遞迴地對子序列進行排序,直到整個序列排序完畢。步驟 1.在序列中選擇乙個關鍵元素作為軸 2.對序列進行重新排序,將比軸小的元素移到軸的前邊,比軸大的元素移動到軸的後面。在進行劃分之後,軸便在它最終的位置上 3.遞迴地對兩個子...