交換排序高速分揀

2021-09-06 23:38:44 字數 4793 閱讀 7653

高速排序(quick sort)也是一種交換排序,它在排序中採取了分治策略。

從待排序列中選取一元素作為軸值(也叫主元)。

將序列中的剩餘元素以該軸值為基準,分為左右兩部分。左部分元素不大於軸值,右部分元素不小於軸值。軸值終於位於兩部分的切割處。

對左右兩部分反覆進行這種切割,直至無可切割。

從高速排序的演算法思想能夠看出,這是一遞迴的過程。

要想徹底弄懂高速排序,得解決兩個問題:

怎樣選擇軸值?(軸值不同,對排序有影響嗎?)

怎樣切割?

問題一:軸值的選取?

軸值的重要性在於:經過切割應使序列盡量分為長度相等的兩個部分,這樣分治法才會起作用。若是軸值正好為序列的最值,切割後,元素統統跑到一邊兒去了,分治法就無效了。演算法效率無法提高。-看別人寫快排的時候,注意他軸值的選取哦。

問題二:怎樣切割?

這涉及到詳細的技巧和策略。在稍後的**中我們一一介紹。

直接選取第乙個元素或最後乙個元素為軸值。這也是國內眾多教材中的寫法。

舉個樣例:

原序列   4   8   12   1   9   6

下標     0   1   2    3   4   5   

軸值 pivot=4

初始化   i                    j

i            j           i不動,移動j,while(i=pivot)j--; 

移動元素 

1   8   12   1   9   6

i        j           j不動,移動i,while(i

移動元素 1   8   12   8   9   6

i,j                   再次移動j,i和j相遇,結束

最後一步 1   4   12   8   9   6   pivot歸位

軸值4的左右兩部分接著切割……

我想你一定看懂了,而且這軸值4,真的沒選好,由於切割後左部分僅僅有乙個元素。

有人稱上面的做法是:挖坑填數。這樣的描寫敘述真的非常形象。簡單解釋下:首先取首元素為軸值,用變數pivot儲存軸值,這就是挖了乙個坑。此時,a[0]就是乙個坑。接著移動j,把合適位置的j填入a[0],於是a[j]成了新的坑。舊的坑被填上,新的坑就出現。直到i和j相遇,這最後乙個坑,被pivot填上。至此完畢了第一趟切割……

看懂了,就動手寫程式吧!

void quicksort(int a, int n)  //高速排序,版本號一

a[i] = pivot; //把軸值放到切割處

quicksort(a, i);

quicksort(a + i + 1, n - i -1);

}}

如今想想以最後乙個元素為軸值的**了,先別急著看,先動動手哦!**例如以下:

void quicksort(int a, int n)

a[i] = pivot; //把軸值放到切割處

quicksort(a, i);

quicksort(a + i + 1, n - i - 1);

}}

為了讓軸值pivot不至於無效(不讓pivot出現最值的情況)。我們能夠使用一些策略來改進pivot的選取。

策略一:

隨機選取序列中一元素為軸值。

int selectpivot(int a, int low, int high)

選取首尾元素不就是該策略的一種特例!

策略二:

隨機選取三數,取中位數。

int selectpivot(int a, int low, int high)

它的一種特例就是,選取原序列首、

尾、中間三數,取它們的中位數。

眼下看來基本經常使用的就這兩種策略。

只是我得吐槽一句:假設原序列中的元素本身就是隨機存放的,也就是說,各個元素出如今各個位置的概率一樣。那麼特別地選取首尾元素和隨機選取又有什麼差別呢?不知大家怎麼看?

還得補充一句:隨機選取軸值後,記得要把它和首或尾的元素交換哦。至於為什麼?***!

這也是《演算法導論》上的版本號。它的普遍做法是選取尾元素為pivot。重點是使用了乙個切割函式:partition()。

偽**與例如以下:

partition(a, low, high)

1. pivot <- a[high]    //選取尾元素為軸值

2. i <- low-1          //把low-1賦值給i,下同

3. for j <- low to high-1    //j的變化範圍[low, high-1]

4.      do if a[j] <= pivot

5.            then i <- i+1

6.            exchange a[i]<->a[j]

7. exchange a[i+1} <

-> a[high]

8. return i+1;    //返回的是切割的位置

然後,對整個陣列進行遞迴排序:

quicksort(a, low, high)

1  if low < high

2  then q <- partition(a, low, high)  //對元素進行切割就在這裡

3  quicksort(a, low, q - 1)

4  quicksort(a, q + 1, high)

假設你不習慣於看偽**,我來舉個樣例:(還是上面的序列)

原序列   4   8   12   1   9   6

下標  -1 0   1   2    3   4   5   軸值pivot是6

初始化 i j                        a[j]=a[0]=4<6,下一步先 i++;再swap(a[i],a[j]);隨後j++;

交換     4   8   12   1   9   6

i   j                    接著移動j

i            j           a[j]=a[3]=1<6,下一步…

交換     4   1   12   8   9   6

i            j       

i                j   

交換     4   1   6    8   9   12  最後一步 swap(a[i+1], a[high]);或者是 swap(a[i+1], a[j]);

所以最後返回的是 i+1

用大白話講講上面的排序過程:用兩個指標i,j,它們初始化為i=-1;j=0,接著讓j不斷地自增,遇到a[j]>pivot就與i交換,直到j指向末尾。

更直白的話:從頭開始遍歷原序列,遇到小於軸值的就交換到序列前面。

看懂了,就寫**了…

int partition(int a, int low, int high)

swap(a[++i], a[high]); //主元歸位

return i; //上面一步已經 ++i,所以這裡不用 i+1

}void quicksort(int a, int low, int high)

}void quicksort(int a, int n)

題外話:看到有的api設計是這種:quicksort(int a, int low, int high)。竟然讓使用者多寫乙個0!如此不為使用者考慮。應越簡潔越好。排序僅僅給陣列名和陣列大小,就可以。

對上面的流程再思考:看到初始化i=-1;你不認為奇怪嗎?為什麼i一定要從-1開始,細緻了解了i的作用,你會發現i本能夠從0開始。這種做法的partition()方法是這種:

int partition(int a, int low, int high)

swap(a[i], a[high]); //主元歸位

return i;

}

再思考:為什麼j不能指向high?若是更改if(a[j]

此時的partition()是這種:

int partition(int a, int low, int high)

return i-1; //這裡為什麼是i-1,得想明確?

}

至於有時候把quicksort()和partition()寫成乙個函式,那是再簡單只是的事情,你肯定會的。

上面用的都是遞迴的方法,把遞迴轉化非遞迴總是不簡單的,也總讓人興奮。這個版本號就是高速排序的非遞迴寫法;

void quicksort(int a, int low, int high)

if (mid + 1 < high)

//僅僅要棧不為空,說明仍有可切割的部分

while(!s.empty())

if (mid + 1 < h)

} }}

這個非遞迴的寫法是非常有意思的,非常須要技巧。細緻想想,你能明確的。

高速排序號稱高速搞定,時間複雜度是o(nlogn)。基本上是最優的排序方法。它的寫法不外乎以上三種,大同小異。看到這裡。你一定徹底了解了它。以上寫法,都經過了本人測試,不知道你的測試是否和我一樣?

若是有所幫助,頂乙個哦!

本專欄的資料夾

全部內容的資料夾

排序 交換排序

交換排序 利用交換位置進行排序 1 演算法思想 重複的走訪要排序的元素,依次比較相鄰兩個元素的大小,如果順序錯誤則交換這兩個元素的位置,直到不需要在比較 2 步驟 比較相鄰兩個元素,如果前乙個比後乙個大,則交換位置 從第乙個元素一直比較到最後乙個元素,這時最後乙個元素的值是最大的 減掉最後乙個元素,...

歸併 高速分揀

我們都知道stl排序庫函式最常用的 sort v.begin biend 這是由於在以公升序。我一般不喜歡用乙個迭代器,我一般用這種格式。排序的陣列,替代指標迭代器。sort a,a n 公升序排序 sort a,a n,cpm int cmp type a,type b 這裡我們來手動實現歸併排序...

交換排序演算法

快速排序 為啥叫快速排序,因為速度快,效率高 1.先找乙個數作為基準。作為基準的這個數,一趟排下來,左邊的數必小於它,右邊的數必大於它,也就是說,它找到了自己的位置。2.將兩個指標i,j分別指向表的起始 基準 和最後的位置。3.比較j指標的數字是否小於基準,j 直到j小於基準,交換位置 4.比較i指...