1. 程式人生 > >數據結構(三) 用java實現七種排序算法。

數據結構(三) 用java實現七種排序算法。

得到 最簡 上傳 根節點 位置 中間 log 說明 堆排序

      很多時候,聽別人在討論快速排序,選擇排序,冒泡排序等,都覺得很牛逼,心想,臥槽,排序也分那麽多種,就覺得別人很牛逼呀,其實不然,當我們自己去了解學習後發現,並沒有想象中那麽難,今天就一起總結一下各種排序的實現原理並加以實現。

                        -WH

一、文章編寫風格總覽

    選擇排序、插入排序、冒泡排序、歸並排序、快速排序、希爾排序、堆排序、

    最後對各種排序算法進行比較,理清楚各種排序的優缺點。  

    其中快速排序是冒泡排序的增強,堆排序是對選擇排序的增強,希爾排序是對插入排序的增強,這就6種了,最後一種就是歸並排序。

          技術分享

二、選擇排序

    選擇排序是我認為最簡單的一種排序了,因為我們自己也很容易想到這種方法對數組進行排序,原理非常簡單,

      技術分享

          原理圖如上所示:先將第一個位值上的數跟之後所有位置上的數依次進行比較,如果第一個位置上的數比第二個位置上的數大,則進行互換,然後繼續將第一個位置上的數與第三個位置上的數進行比較,經過一輪的比較後,第一個位值上的數就是所有數中最小的一個,接著將第二個位置上的數與之後所有位置上的數進行比較,同樣的規則,第二輪比較結束後,第二位放的就是所有數中第二小的數,依次往下比,直到最後一個位置結束。按照這種方法進行排序,就叫做選擇排序。

          代碼實現

            技術分享

          測試

            技術分享

技術分享 View Code

              

        舞蹈:http://v.youku.com/v_show/id_XMjU4NTY5NTcy.html

三、插入排序

      簡單,給定的一組記錄,將其分為兩個序列組,一個為有序序列(按照順序從小到大或者從大到小),一個為無序序列,初始時,將記錄中的第一個數當成有序序列組中的一個數據,剩下其他所有數都當做是無序序列組中的數據。然後從無序序列組中的數據中(也就是從記錄中的第二個數據開始)依次與有序序列中的記錄進行比較,然後插入到有序序列組中合適的位置,直到無序序列組中的最後一個數據插入到有序序列組中為止。  

      原理圖

           技術分享  

      代碼實現

              技術分享

技術分享 View Code

        很有趣的視頻,插入排序的趣味舞蹈:http://v.youku.com/v_show/id_XMjU4NTY5MzEy.html

四、冒泡排序

      冒泡排序跟選擇排序一樣的簡單,好理解,整個過程就想氣泡一樣往上升,假設從小到大排序,對於給定的n個記錄,從第一個記錄開始依次對相鄰的兩個記錄進行比較,當前面的記錄大於後面的記錄時,交換位置,進行一輪比較後,第n位上就是整個記錄中最大的數,然後在對前n-1個記錄進行第二輪比較,重復該過程直到進行比較的記錄只剩下一個為止。

              技術分享

        代碼實現

          技術分享

技術分享 View Code

  

      冒泡排序:http://v.youku.com/v_show/id_XMzMyOTAyMzQ0.html

五、歸並排序

      歸並排序有兩種實現方式,一種是非遞歸的,一種是遞歸的,但是我覺得如果你理解了非遞歸的實現,那麽你就知道了歸並排序的原理,而遞歸的也就非常簡單了。

       什麽是歸並排序呢?(我們講解的是2路歸並排序)

           一張圖就可理解什麽叫做2路歸並排序

               技術分享

           初始將一個數組中每個元素都看成一個有序序列(數組長度為n),然後將相鄰兩個有序序列合並成一個有序序列,第一趟歸並就可以得到n/2個長度為2(最後一個有序序列的長度可能是1,也可能不是,關鍵看數組中元素的個數了)的有序序列,在進行兩兩歸並,得到n/4個長度為4的有序序列(最後一個的長度可能小於4)...一直這樣歸並下去,直到得到一個長度為n的有序序列1

       簡單來說,通過三步,解決三個問題,就可以寫出歸並排序      

          1、解決相鄰兩個有序序列歸並成一個有序序列,非常簡單,新增一個數組(長度和需要排列的數組相同),

              二路歸並的核心操作,在歸並的過程中,可能會破壞原來的有序序列,所以,將歸並的結果存入另外一個數組中,設兩個相鄰的有序序列為r[s] ~r[m]和r[m+1]~r[t],將這兩個有序序列歸並成一個有序序列,r1[s]~r1[t],設三個參數i,j,k。 i和j分別指向兩個有序序列的第一個記錄,即i=s,j=m+1,k指向存放歸並結果的位置(也就是將歸並結果放到r1中的哪個位置)k=s。然後,比較i和j所指記錄的數,取出較小者作為歸並結果存入k所指的位置,然後將較小者的指向往後移動,直至兩個有序序列之一的所有記錄都取完,在將另一個有序序列的剩余記錄順序送到歸並後的有序序列中(也就是放到r1中)

             技術分享 

技術分享 View Code

          2、如何完成一趟歸並?

             這裏就需要分情況了,三種情況,

               假設每個有序序列中的元素個數為h(第一次歸並的h=1),i=0,從第一個元素開始。歸並每次取兩個有序序列,那麽跨度就是2h,問題就來了,只要知道長度為n(n為數組的最大下標值)的數組中有幾個這樣的兩個有序序列,那麽可以進行不同的操作了。

             第一種情況:(i+2*h-1) <= n //比如,i=0,h=1時,(i+2*h-1)的意思就是指向了第一個兩個有序序列的最後一個位置的下標值,用它來跟n(n為數組最大的下標值)比較,如果小於n,那麽說明後面還有別的數,如果等於n,說明到結尾了,整個數組正好全是兩個有序序列得,不會有多余數。那麽就執行一次歸並,將這兩個有序序列歸並,然後i加2h。如果還符合這個條件,繼續歸並,如果不符合,判斷別的情況。

              第二種情況:(i+h-1) < n //說明最後還有兩個有序序列,但是最後一個有序序列的長度不是h,同樣將其進行歸並

              第三種情況: (i+h-1) >= n //說明只剩下最後一個有序序列,則直接將其有序序列送到r1的相應位置。

            技術分享

技術分享 View Code

        3、完成整個歸並排序

            前面我們解決了兩個問題,一個是兩個有序序列如何進行歸並,一個是如何判斷完成一趟歸並過程。現在就需要解決如何控制二路歸並的結束呢?也就是需要歸並多少趟。

            當步長等於n或者大於n時,說明只剩下一個有序序列了,那麽即歸並結束了。

            技術分享

技術分享 View Code

          

六、快速排序

      快速排序是對冒泡排序的增強,增強得點在於:冒泡排序中,記錄的比較和移動是在相鄰兩個位置進行的,記錄每次交換只能後移一個位置,因而總的比較次數和移動次數較多,而快排記錄的比較和移動是從兩端向中間進行的,較大的記錄一次就能從前面移動到後面,較小的記錄一次就能從後面移動到前面,這樣就減少了比較次數和移動次數

      快速排序原理:選取一個軸值(比較的基準),將待排序記錄分為獨立的兩個部分,左側記錄都是小於或等於軸值,右側記錄都是大於或等於軸值,然後分別對左側部分和右側部分重復前面的過程,也就是左側部分又選擇一個軸值,又分為兩個獨立的部分,這就使用了遞歸了。到最後,整個序列就變得有序了。

      問題:如何選擇軸值?如何將序列變成左右兩部分?

      軸值的選擇有三種:

            1、選取序列的第一個位置上的記錄

            2、選擇序列的中間位置上的記錄

            3、將序列第一個位置 和 中間位置 和 末尾位置上的記錄進行比較,選擇大小居中的記錄,

      如何將序列劃分成左右兩部分?         

        看圖的執行流程,當一趟比較下來,軸值的左側和右側就被排好了,其中利用了first和end兩個參數,一個從起點開始,一個從末尾開始,當兩個相等時,就將序列中所有記錄都遍歷了一遍,第一次的比較次數是和選擇排序第一次比較次數是一樣的,但是之後就開始不一樣了,因為在軸值的左側的元素就不用跟軸值右側的元素進行比較了,而選擇排序還是跟所有的比。

            技術分享

            技術分享

      遞歸調用

            技術分享  

技術分享 View Code

          快速排序:http://v.youku.com/v_show/id_XMzMyODk4NTQ4.html

七、希爾排序

      希爾排序其實是插入排序的升級版本,本質上進行的也是插入排序的操作,但是希爾排序並不是把一組記錄看成一個整體,而將整個記錄分為了若幹組記錄,然後在對每組記錄進行插入排序,

      分組規則為如下所示:假設有 1 2 3 4 5 6 7 8 9 10 十個位置(每個位置上都會放數,這裏忽略數,只討論位置)。(省略了插入排序操作,只對如何分組進行講解,而完整的希爾排序就是在每次分組完之後進行插入排序操作即可)

        步長為:5、3、1

        第一次分為5組記錄(組數跟步長是一樣的):1,6 、2,7、3,8、 4,9、 5,10 這五組記錄,分別對這五組記錄進行插入排序。

        第二次分為3組記錄:1,4,7,10、2,5,8、3,6,9 這三組記錄,分別對這三組記錄進行插入排序

        第三次分為1組記錄:1 2 3 4 5 6 7 8 9 10, 為這組記錄進行插入排序,

             而步長只要滿足最後一次為1,並且是從大到小即可。一般使用(數組長度/2) 或者 (數組長度/3 +1) 來代表步長。

          這樣做的好處是:

                將待排序的數組元素分成多組,每組中記錄數相對較少

                經過前幾次的排序後,整個序列變為了“基本有序序列”,最後在對所有元素進行一次直接插入排序。

            直接插入排序對基本有序和記錄數少的序列的效率是非常高的,而希爾排序就是利用了這兩點。

      原理圖

          技術分享

            解釋:第一次分組,49,13、38,27、65,49、97,55、76,04 五組,對這五組分別進行插入排序,在49找到13時,就會進行插入排序,位置會進行互換,而並非先全部分組,後排序。

               按照步長一直重復執行,直到步長為1後,執行完最後一次直接插入排序,整個希爾排序就完成了。    

      運行時動態圖:看最下面我分享的資源即可。(不知道博客園中如何上傳.swf文件,或者動態圖也不會如何上傳。無奈)        

      代碼實現

           技術分享  

技術分享 View Code

    

      希爾排序舞蹈:http://v.youku.com/v_show/id_XMjU4NTcwMDIw.html

八、堆排序

    上面說的希爾排序是對插入排序的增強,那麽堆排序呢,就是對選擇排序進行增強,選擇排序一個數據要跟每個數據都進行一次比較,並沒有利用到一些比較的結果,比如,4 跟10比較,3跟4比較後,按理說不用讓3跟10在比了,但是選擇排序並沒有這種智能化,而是老老實實的比較,而堆排序就完美的利用了前幾次比較的結果,從而增加了效率。

     講解堆排序之前,必須要知道什麽是堆?

        堆是一顆完全二叉樹,什麽是完全二叉樹?只有最下面的兩層結點度能夠小於2,並且最下面一層的結點都集中在該層最左邊的若幹位置的二叉樹(還不懂就去百度一下什麽是完全二叉樹)

                技術分享  

        這個圖是一個完全二叉樹,但不是堆。

        堆分兩種,大頂堆和小頂堆

           大頂堆:在完全二叉樹的基礎上,每個父節點都比自己的兩個子結點大,這樣的就是大頂堆,特點是根節點是最大的值,看下圖,90比70,80大,70比60,10大,以此類推

               技術分享  

            小頂堆:和大頂堆相反,根節點是最小的值,並且每個父結點都比自己的子節點要小,如下圖

               技術分享 

            堆排序就是利用堆的這種特點進行編寫的,原理:先將一組擁有n個元素的序列構建成大頂堆或者小頂堆,在將根結點上的數跟堆最後一位數進行互換,此時,第n位的數就是整個序列中最大或者最小的數了,然後在將前n-1位元素進行構建成大頂堆或者小頂堆,在將根結點跟第n-1位進行互換,得到第2大或者第2小的數,在將前n-2位數進行構建,依次類推,直到只剩下1位元素即結束,排序完成。

            通過講解原理:堆排序分為三步

               1、構建大頂堆或小頂堆

               2、循環

                   根節點和末尾結點進行互換,

                   構建大頂堆或小頂堆 

               3、排序完成

            技術分享

            技術分享

技術分享 View Code

                  

九、總結比較各種排序算法的優缺點

                技術分享

          

          1、註意,排序的穩定性的意思是:舉例說明。

                排序前:5,6(1),1,4,3,6(2),(第一個6在第二個6之前)

                排序後:如果排序後的結果是1,2,3,4,5,6(1),6(2)那麽就說此排序算 法是穩定的,反之為不穩定

          2、當待排序記錄個數n較大,並且是無序序列,對穩定性不作要求時,采用快速排序為宜

          3、當待排序記錄個數n較大,內存空間允許,要求排序穩定時,采用歸並排序為宜

          4、當待排序記錄個數n較大,且序列中可能出現正序或逆序的情況,不要求穩定性,采用堆排序或歸並排序為宜

          5、等等。。。這種直接到百度上一搜,肯定會有人總結的,並且總結的肯定比我好,我就了解各種排序算法原理,如何用java進行實現,和基本的一點特性。對自己要求更高的同學則需要繼續深入研究一下。。

數據結構(三) 用java實現七種排序算法。