玩轉快速排序

2021-09-24 13:45:33 字數 3884 閱讀 5130

目錄

快速排序基本原理

快速排序兩種演算法

非遞迴實現快速排序 總結

(本文章僅作為記錄及總結)

簡單的說,快速排序(這裡是公升序)就是:

①確定乙個樞紐元素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.遞迴地對兩個子...