算法之排序(中)

文章來源:http://blog.seclibs.com/算法之排序中/算法

上一篇文章說了時間複雜度爲O(n2)的冒泡、插入和選擇三個排序方式,它們只適合在數據規模比較小的時候,接下來要說的是兩個時間複雜度爲O(nlogn)的算法,歸併排序快速排序,它們比較適合在大規模數據的時候使用,相比於前面的三個算法就更加經常使用。數組


首先來看歸併排序(Merge Sort)數據結構

歸併排序的操做仍是比較簡單的,它是將數組從中間分割爲先後兩部分,而後再將每一部分分割,直到分割到不能分爲止,而後將數值兩兩排序合併,不斷合併到最初的數組,就完成了排序的操做,它的示例圖是這樣的函數

歸併排序使用的是分治思想,分而治之,也就是將大問題不斷的分解,分解成一個個小問題,小問題都解決了,那大問題也就都解決了,這個跟前面說的遞歸是特別像的,分治是一種處理問題的思想,遞歸是具體的技巧,分治算法通常都是用遞歸來實現的。ui

既然是使用遞歸來實現的,那咱們就要找到它的通用遞歸公式和遞歸終止條件,首先咱們假設第一個數值用p表示,最後一個數值用r表示,既然要將數組從中間分割,那每次分割的下標就爲q=(p+r)/2,即數組就分爲了兩段(p,q)和(q+1,r)。code

那遞歸終止條件又該怎麼定義?咱們來看何時是咱們須要截至的,當數組分解成單個字符的時候就中止了,那這個時候開始結束符號將一致,因此只須要在p>=r時結束就能夠了。blog

分解結束以後就須要返回進行合併了,這個時候咱們可使用兩個變量來幫助咱們進行識別,這裏用i和j兩個變量來進行說明,將i和j分別指向(p,q)和(q+1,r)的第一個元素,同時比較這兩個元素的大小,若是a[i]<a[j],就將a[i]放到一個臨時數組中,並將i後移一位;不然就將a[j]放到臨時數組中,並將j後移一位,最後將臨時數組拷貝回咱們的數組就完成了排序操做。 說了這麼多理解起來也比較困難,有一句話就很符合如今的狀況,talk is cheap. show me the code( 不要多bb,放碼過來吧 ),代碼在後一片文章在給出來。 那歸併排序是否是原地排序算法呢,因爲歸併排序在每次合併數據的時候都須要額外申請一個空間來存放,在合併完成以後將會被釋放掉,而且在任意時刻,cpu只會有一個函數在執行,也就只有一個臨時的內存空間在使用,臨時空間最大也超不過數據總和n,因此空間複雜度爲o(n),不是一個原地排序算法。 在合併過程當中,若是(p,q)、(q+1,r)中存在相同的元素,那咱們就能夠先把(p,q)中的元素先放入到臨時空間中,因此歸併排序是一個穩定的排序算法。 * 接下來,咱們來看快速排序(quicksort),簡稱「快排」,它也是利用分治思想,雖然看起來跟歸併排序有點像,可是它們的思路是徹底不同的。 快速排序是在p到r中任意選擇一個pivot(分區點),而後遍歷p到r的數據,將小於 pivot 的放到左邊,將大於 的放到右邊,這樣就將數組分紅了三個區域,p到q-1是小於 的,q+1到r是大於 的,中間q是 。 ![](http: blog.seclibs.com wp-content uploads 2020 02 4d892c3a2e08a17f16097d07ea088a81.jpg) 而後不斷的分治,直到區間縮小爲1,因此它的遞歸終止條件也是p>=r。排序

在選取 pivot 的時候,通常選擇最後一個數值,選擇中間的數值做爲 pivot 後,還須要將它移動到頭或尾的位置,若是咱們在進行分區的時候,不考慮空間大小的因素的話,分區操做實際上是很是好寫的,每一次分區都新申請兩個空間,將小於 pivot 的放到一個空間,將大於 pivot 的放到另外一個空間,而後再將兩個空間合起來拷貝回前面的數組空間,這樣的話快排就不是原地排序算法了,因此咱們採起另一種比較巧妙的辦法。遞歸

這個操做有點相似上一篇文章中的選擇排序,咱們經過一個變量i,把數組分爲先後兩個區域,用選擇排序中的叫法,前面是已排序區間,後面是未排序區間,咱們每次都將未排序區間中的一個數值與 pivot 進行比較,若是小於 pivot 就將它放到已排序區間的末尾,也就是變量i所指向的位置,這個時候咱們採起數組中用到的數據交換的方式,將須要調動的數據與i所指位置交換,這樣涉及到的操做也就僅僅是數值交換的問題了,空間複雜度也就變成了O(1),快速排序也就是一個原地排序算法了。內存

同時也由於其中直接使用了數據交換的方式,他也就改變了數據原來的順序,也就不是一個穩定的排序算法了。


那快速排序和歸併排序的區別到底在哪裏,它們在於歸併排序是自下而上的,先處理子問題,而後再合併;快速排序是自上而下的,先分區,而後再處理子問題。


參考文檔

極客時間-數據結構與算法之美


文章首發公衆號和我的博客

公衆號:無意的夢囈(wuxinmengyi)

博客:http://blog.seclibs.com/</a[j],就將a[i]放到一個臨時數組中,並將i後移一位;不然就將a[j]放到臨時數組中,並將j後移一位,最後將臨時數組拷貝回咱們的數組就完成了排序操做。>

相關文章
相關標籤/搜索