【数据结构】快速排序非递归算法及其改进
在學數(shù)據(jù)結構中排序這一章節(jié)的時候,有一道有關快速排序的作業(yè)題描述如下:
按下述要求編寫快速排序的非遞歸算法:
定義一個棧(或隊列),把整個序列的上、下界入棧(或隊列)。當棧(或隊列)非空時進行如下操作:
(1)取棧頂(或隊頭)元素作為序列的上、下界,在區(qū)間的頭部、中間、尾部取關鍵字居中的元素作為中樞元素,進行一趟快速排序;
(2)在一趟排序過程中,如果子表已有序(沒有發(fā)生元素交換),則該子序列排序結束,否則先對劃分出的長度較短的子表進行排序,且將另一子表的上、下界入棧(或隊列)保存;
(3)若待排序區(qū)間中數(shù)據(jù)元素數(shù)小于等于3,則不再進行分割,而是直接進行比較完成排序。
完成算法實現(xiàn)后,用大數(shù)據(jù)量進行測試,同原有快速排序算法在時間上進行對比分析。
這道題本質上是在優(yōu)化快速排序。
1.用棧代替遞歸
函數(shù)的遞歸本身就利用了堆棧,用棧改寫遞歸算法為非遞歸是常用思路。我們可以將序列的上下界入棧,排序時再取棧頂元素并出棧,本題中要求先對劃分出的長度較短的子表進行排序,那么我們就先入棧較長的子表,將較短的子表后入棧以便下次循環(huán)中優(yōu)先出棧。(部分)代碼如下:
SeqStack<int> s; while( !s.isEmpty()) //棧非空且未排好序{//得到某分區(qū)的左右邊界int r;s.getTop(r);s.pop();int l;s.getTop(l);s.pop();boundary = Part(a, l, r, Flag); //boundary為樞軸元素所在位置if( boundary - l < r - boundary) ///如果左分區(qū)比右分區(qū)短{if( boundary + 1 < r ) //判斷右分區(qū)是否存在{s.push(boundary + 1);s.push(r);}if( boundary - 1 > l ) //判斷左分區(qū)是否存在{//將左分區(qū)端點后入棧以便優(yōu)先排序s.push(l);s.push(boundary - 1);}}else //如果右分區(qū)比左分區(qū)短{if( boundary - 1 > l ) //判斷左分區(qū)是否存在{s.push(l);s.push(boundary - 1);}if( boundary + 1 < r ) //判斷右分區(qū)是否存在{///將右分區(qū)端點后入棧以便優(yōu)先排序s.push(boundary + 1);s.push(r);}}}2.中樞元素的取法
一般書本上的快速排序是取第一個待排序的元素作為中樞元素,而為了優(yōu)化快速排序算法,如果能夠盡量將中樞元素取在整個待排序數(shù)組的中位數(shù)附近,那么兩個子表的長度就會接近,這樣一來就減少了時間復雜度,優(yōu)化了快速排序算法,本題中采取的方法是:在區(qū)間的頭部、中間、尾部取關鍵字居中的元素作為中樞元素。其實我們也可以平均取五個點、七個點來找居中的元素。(頭中尾取中值)代碼如下:
int Mid(int first, int mid, int last) //求頭,中,尾中關鍵字居中的元素 {if ((first>=mid&&first<=last)||(first<=mid&&first>=last))return first;else if ((mid>=first&&mid<=last)||(mid<=first&&mid>=last))return mid;elsereturn last; }注意,要在Partition函數(shù)中交換首元素和中樞元素的位置:
int pivot = Mid(low, (low+high)/2, high);//選擇區(qū)間的頭部、中間、尾部取關鍵字居中的元素作為中樞元素Swap(elem[pivot], elem[low]); //保持代碼的統(tǒng)一性3.在一趟排序中,如果子表已有序,則該子序列排序結束
這一點便于理解,代碼實現(xiàn)上可以定義一個bool類型的標記,來判斷Partition中是否發(fā)生過元素交換,如果沒有交換bool賦true。
4.待排區(qū)間中元素數(shù)<=3,則不再進行分割,則直接比較排序
快速排序是適用于大數(shù)據(jù)量的排序方式,在數(shù)據(jù)量比較小的時候,使用插入排序或者選擇排序較好,所以很多實用的排序算法使用快排+插排的方式排序。本題中要求:若待排序區(qū)間中數(shù)據(jù)元素數(shù)小于等于3,則不再進行分割,而是直接進行比較完成排序,代碼如下:
void JustSort(int a[], int left, int right) //數(shù)組元素小于等于3時的直接比較排序 {if(right-left==1) //序列只有兩個元素時{if(a[left] > a[right]){Swap(a[left], a[right]);}}else //三個元素時{if(a[left] > a[left+1])Swap(a[left], a[left+1]);if(a[left+1] > a[right])Swap(a[left+1], a[right]);if(a[left] > a[left+1])Swap(a[left], a[left+1]);} }應用以上優(yōu)化方法后,用50000個隨機元素的數(shù)組進行排序,與書本上的遞歸快排進行時間比較,如下:
可見有一定的優(yōu)化效果。
全部代碼如下:
#ifndef __ExQUICKSORT_H__ #define __ExQUICKSORT_H__ #include "SeqStack.h"int Mid(int first, int mid, int last) ///求頭,中,尾中關鍵字居中的元素 {if ((first>=mid&&first<=last)||(first<=mid&&first>=last))return first;else if ((mid>=first&&mid<=last)||(mid<=first&&mid>=last))return mid;elsereturn last; }int Part(int elem[], int low, int high, int &flag) ///參數(shù)flag用以判斷是否已經(jīng)有序,以便結束排序 //原快速排序算法中的劃分部分,寫成函數(shù)方便循環(huán)調用 {int pivot = Mid(low, (low+high)/2, high);//選擇區(qū)間的頭部、中間、尾部取關鍵字居中的元素作為中樞元素Swap(elem[pivot], elem[low]); /// 交換樞軸元素和首元素的位置,保持代碼的統(tǒng)一性int e = elem[low]; // 取樞軸元素int i = low, j = high;while (i < j){while (i < j && elem[j] >= e) // 使j右邊的元素不小于樞軸元素j--;if (i < j){elem[i++] = elem[j];flag = 1;}while (i < j && elem[i] <= e) // 使i左邊的元素不大于樞軸元素i++;if (i < j){elem[j--] = elem[i];flag = 1;}}elem[i] = e;return i; //返回樞軸元素位置 }void JustSort(int a[], int left, int right) ///數(shù)組元素小于等于3時的直接比較排序 {if(right-left==1) //序列只有兩個元素時{if(a[left] > a[right]){Swap(a[left], a[right]);}}else //三個元素時{if(a[left] > a[left+1])Swap(a[left], a[left+1]);if(a[left+1] > a[right])Swap(a[left+1], a[right]);if(a[left] > a[left+1])Swap(a[left], a[left+1]);} }template <class ElemType> void ExQuickSort(ElemType a[], int left, int right) // 操作結果:對數(shù)組elem[low .. high]中的元素進行快速排序 {int Flag = 0; ///標記,用來判斷是否已經(jīng)排好序(未發(fā)生過交換)SeqStack<int> s;if( left<right ){if(right-left < 3) //如果序列小于等于3個元素{JustSort(a, left, right);return ;}int boundary = Part(a, left, right, Flag); //劃分后的中樞所在位置if(Flag == 0)return ;if( boundary - left < right - boundary) ///如果左分區(qū)比右分區(qū)短{if( boundary + 1 < right ) //判斷右分區(qū)是否存在{s.push(boundary + 1);s.push(right);}if( boundary - 1 > left ) //判斷左分區(qū)是否存在{///將左分區(qū)端點后入棧以便優(yōu)先排序s.push(left);s.push(boundary - 1);}}else ///如果右分區(qū)比左分區(qū)短{if( boundary - 1 > left ) //判斷左分區(qū)是否存在{s.push(left);s.push(boundary - 1);}if( boundary + 1 < right ) //判斷右分區(qū)是否存在{///將右分區(qū)端點后入棧以便優(yōu)先排序s.push(boundary + 1);s.push(right);}}while( !s.isEmpty()) //棧非空且未排好序{//得到某分區(qū)的左右邊界int r;s.getTop(r);s.pop();int l;s.getTop(l);s.pop();boundary = Part(a, l, r, Flag); //boundary為樞軸元素所在位置if(Flag == 0) ///如果已經(jīng)有序,結束排序。return ;if(right-left < 3) //如果序列小于等于3個元素{JustSort(a, left, right);return ;}if( boundary - l < r - boundary) ///如果左分區(qū)比右分區(qū)短{if( boundary + 1 < r ) //判斷右分區(qū)是否存在{s.push(boundary + 1);s.push(r);}if( boundary - 1 > l ) //判斷左分區(qū)是否存在{///將左分區(qū)端點后入棧以便優(yōu)先排序s.push(l);s.push(boundary - 1);}}else ///如果右分區(qū)比左分區(qū)短{if( boundary - 1 > l ) //判斷左分區(qū)是否存在{s.push(l);s.push(boundary - 1);}if( boundary + 1 < r ) //判斷右分區(qū)是否存在{///將右分區(qū)端點后入棧以便優(yōu)先排序s.push(boundary + 1);s.push(r);}}}} }#endif // __ExQUICKSORT_H__總結
以上是生活随笔為你收集整理的【数据结构】快速排序非递归算法及其改进的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不孕不育检测方式
- 下一篇: 【数据结构】对快速排序原理的理解(图解,