1. 程式人生 > >排序演算法,按照自己的理解描述的

排序演算法,按照自己的理解描述的



1> 插入排序:

 基本思想: 把需要排序的集合分為兩部分,一部分是排序好的,一部分未排序的。就像打撲克一樣,如果你習慣從左到右按從小到達的方式排列,開始都背面蓋著放在桌上,然後抓起一張,抓第二張時,先和手上的第一張對比,如果點數大於第一張,那就把它插到第一張的右邊。剩下桌上的牌都按照這個方式去處理。

 實際上,在用java陣列處理的時候,還是和玩撲克是有區別的。撲克的左右沒有邊界限制,而實際陣列有邊界限制,所以,需要考慮到邊界的問題,所以最好是從右到左查詢過去,不會產生越界的問題。

------------------用java陣列進行實現------------------------

從右到左查詢

---------------------------code-begin-----------------------

public static int[] insertSort(int[] rawArr) {

       // 常識性的判斷,不要沒有牌或者牌只有1張

       if (rawArr == null || rawArr.length < 2) {

           return rawArr;

       }


       //從第二個數開始,第一張已經抓在手上了

       for(int i = 1 ; i < rawArr.length ; i++ ) {

            int j = i - 1 ; //對比手上的牌,最右邊的那張

            int insertVal = rawArr[i] ; //待插入的牌


            while(j > -1 && rawArr[j] > insertVal) { //如果牌比待插入的大,往左邊繼續看

                 rawArr[j+1] = rawArr[j]; //把牌向右挪一位

                 j-- ; //檢查左邊的牌

            }


            rawArr[j+1] = insertVal ; //最後,把牌插入正確的位置

            //到下一個迴圈,也就是下一張沒上來的牌。

       }

               return rawArr ;


   }

----------------------------code-end------------------------


2> 氣泡排序:

基本思想:

  可以想想成陣列的樣子是從上到下的。如果排序是從大到小,那麼就是從底部開始兩兩對比,哪個數大就放到上面。這樣經過第一輪的對比,最大的數就會“浮”到最上面,也就是第一個位置。第二輪的時候,最上面的數就不用對比了,第二輪完畢之後,倒數第二大的數就會“浮”到倒數第二個位置。依次類推,最後迴圈完,就會按從大到小的順序排列了。


-------------------------code-begin-------------------------

   public int[] popUpSort(int[] rawArr) {

       if(rawArr == null || rawArr.length < 2) {

           return rawArr ;

       }


        //有n個數,就是執行n-1次,最後一個數就不用比對了

       for(int i = 1 ; i < rawArr.length ; i++) {

           //兩個兩個對比,從下往上,到排序完的那個數為止(不包含)

           for(int j = 0 ; j < rawArr.length - i ; j ++) {

               if(rawArr[j] < rawArr[j+1]) {

                   int tmp = rawArr[j];

                   rawArr[j] = rawArr[j+1];

                   rawArr[j+1] = tmp ;

               }

           }


       }


       return rawArr ;


   }

-------------------------code-end---------------------------


3> 歸併排序

基本思想:

  一個無序的陣列,假如要從小到大進行排序。先分割成兩部分,然後每部分再進行分割,直到分割的部分只有一個元素時,就認為它是有序的。這時候,再進行合併,合併的流程就是新建一個臨時陣列,長度是兩個子陣列長度的和。然後分別掃描兩個子陣列(a和b),如果a[0]小於b[0],那就把a[0]先加到臨時陣列中(反之,就是把b[0]先加到臨時陣列中),然後a陣列向前移動一位,然後a[1]再與b[0]比較,比較小的就加到臨時陣列中。以此類推,當有一個數組新增完畢之後,而另一個數組有所剩餘,就把剩下的元素都加到臨時陣列中,這樣就完成了排序。然後遞迴的返回,然後合併,最終就得到整個陣列的排序後的陣列。

-------------------------code-begin-------------------------

   public int[] mergeSort(int[] rawArr) {

       if(rawArr == null && rawArr.length < 2) {

           return rawArr;

       }


       return merge(rawArr);


   }


   private int[] merge(int[] slice) {

       //只有一個元素,直接就是排序好的

       if(slice.length == 1) {

           return slice;

       }

       //切成兩部分

       int half = slice.length / 2 ;

       int[] aArr = getSubArrayOf(slice,0,half);

       int[] bArr = getSubArrayOf(slice,half,slice.length);

       //遞迴呼叫,這樣aArr和bArr就是排序好的了

       aArr = merge(aArr) ;

       bArr = merge(bArr) ;

       //下面就進行合併

       int aLength = aArr.length ;

       int bLength = bArr.length ;

       int totalLength = aLength + bLength ;

       int[] mArr = new int[totalLength];

       int i = 0 ;

       int j = 0 ;

       int k = 0 ;

       //掃描兩個子陣列,根據條件進行排序

       while(i < aLength && j < bLength) {

           if(aArr[i] < bArr[j]) {

               mArr[k++] = aArr[i++];

           }else {

               mArr[k++] = bArr[j++] ;

           }

       }

       //如果是a陣列剩餘,就把剩下的加到父陣列中

       while(i < aLength) {

           mArr[k++] = aArr[i++];

       }


       //如果是b陣列剩餘,就把剩下的加到父陣列中

       while( j < bLength) {

           mArr[k++] = bArr[j++];

       }

        //合併好就返回

        return mArr;

   }

   private int[] getSubArrayOf(int[] arr, int startIndex , int endIndex) {

       if(endIndex < startIndex) {

           return null;

       }

        int len = endIndex - startIndex ;

       int[] newArr = new int[len];


       for(int i = startIndex ; i < endIndex ; i++) {

           newArr[i-startIndex] = arr[i];

       }



       return newArr;


   }

-------------------------code-end---------------------------  



4> 堆排序

堆是樹狀結構的,主要有兩個特點:

1) 根節點的值最小(最大)

2)子樹也是一個堆,稱為最小堆(最大堆)

二叉堆是完全二叉樹或者是近似完全二叉樹來構造的堆。

還有其他“堆”(二項式堆,斐波納契堆)



利用堆的結構,比如最小堆,那麼根節點是當前的樹狀結構中值最小的元素。

那麼,如果一組數要從小到大,先把它調節成一個最小堆,然後每次取它的根節點,然後刪除根節點,再對剩下的樹狀結構進行調整,重新成為一個最小堆。然後再取根節點,這樣迴圈的做下去,這樣,最後元素都取完了,那麼也就把資料都從小到大排序好了。

這樣,需要兩個步驟對排序進行輔助。

1)根節點刪除

2)調整成堆


按照陣列的方式儲存堆,那麼節點之間的關係為:

節點位置為i。那麼父節點位置為 i-1 / 2

兩個子節點的位置為 2*i + 1 與 2*i + 2


如果是一個數組,要使用堆的性質進行排序。那麼就是:

1)調整成堆

2)把根節點a[0]和最後一個節點a[n-1]對調。

然後再對a[0]到a[n-2]進行恢復堆,然後再把a[0]和a[n-2]進行對調。

迴圈下去,直到a[0],這樣就排好了。




5> 快速排序

----------------------------------------------------

http://www.cnblogs.com/surgewong/p/3381438.html

----------------------------------------------------

快速排序的基本思想: 通過一次迴圈,把需要排序的元素分割成兩部分A和B,長度不一定相等,但其中A中任何元素的值比B中任何元素的值都要小。

然後再遞迴呼叫,分別再對A和B進行切割,分別分成兩部分,也是一部分中的元素值比另一部分都小。迴圈分割,到只有一個數,那麼每段都是排序好了。

然後再遞迴返回,由於每一段都是已經排序好的,那麼返回的時候,兩段就不需要重新進行比較,直接連線起來就是排序好的。

那麼,具體怎麼在一次迴圈中就把資料分割成滿足要求的兩部分呢?

先選一個值x,然後分別從陣列的兩端進行掃描,比這個數小的就放到這個數的左邊,比這個數大的就放到這個數的右邊。


以下為例:


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 46 | 30 | 82 | 90 | 56 | 17 | 95 | 15 |

--------------------------------------------

 i=0                                          j=7


選第一個數 46 為x , 然後開始比較, a[j] = 15 < 46 ,那麼就交換兩個數的位置,同時i向前移動一位。



  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 82 | 90 | 56 | 17 | 95 | 46 |

--------------------------------------------

       i=1                                    j=7



然後再比較, a[i] = 30 < 46 ,那麼不動,i再移動一位


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 82 | 90 | 56 | 17 | 95 | 46 |

--------------------------------------------

               i=2                           j=7

然後再比較,a[i] = 82 > 46 ,那麼交換兩者的位置,然後j向後移動一位。


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 46 | 90 | 56 | 17 | 95 | 82 |

--------------------------------------------

           i=2                 j=6


然後比較  a[j] = 82 > 46 , 不動,j再向後移動一位


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 46 | 90 | 56 | 17 | 95 | 82 |

--------------------------------------------

               i=2              j=5


然後再比較, a[j] = 17 < 46 ,兩者交換,i向前移動一位


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 17 | 90 | 56 | 46 | 95 | 82 |

--------------------------------------------

                      i=3        j=5

再比較 a[i] = 90 > 46 ,兩者交換,j向後移動一位



  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 17 | 46 | 56 | 90 | 95 | 82 |

--------------------------------------------

                     i=3  j=4


再比較 a[j] = 56 > 46 ,不交換,j向後移動一位


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 30 | 17 | 46 | 56 | 90 | 95 | 82 |

--------------------------------------------

              i=j=3

此時發現i=j=3,這時候,此次分割結束。


這樣,陣列被分割成[0-2][3][4-7]三個區間,然後再遞迴的對[0-2]和[4-7]按照以上方式進行分割。

最後分割成一個個數之後再分別合併起來,組成一個有序的序列。


-------------------------code-begin-------------------------

   private void quick_sort(int[] rawArr,int left , int right) {

       if(left < right) {

           int i = left ;

           int j = right ;

           int x = rawArr[i];

           while( i < j ) {


               while(i < j && rawArr[j] < x) {

                   j-- ;

               }


               if(i < j) {

                   rawArr[i++] = rawArr[j] ;

               }


               while(i < j && rawArr[i] >= x) {

                   i++ ;

               }


               if(i < j) {

                   rawArr[j--] = rawArr[i];

               }


           }


           rawArr[i] = x;


           quick_sort(rawArr,left,i-1);

           quick_sort(rawArr,i+1,right);

       }

-------------------------code-end---------------------------


6> shell排序


通過不斷“對摺”,然後對各個分組進行“插入排序”。

例如以下資料:


有8個數,第一次先取 gap = 4


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 46 | 30 | 82 | 90 | 56 | 17 | 95 | 15 |

--------------------------------------------


這樣資料就分為了(0,4)(1,5)(2,6)(3,7)總共4組。 然後對每組元素分別進行“插入排序”。


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 46 | 17 | 82 | 15 | 56 | 30 | 95 | 90 |

-----------------------------------------


然後再“對摺”,gap = gap / 2 = 2。這樣分為了2組。

(0,2,4,6)(1,3,5,7) ,然後再對每組進行“插入排序”。


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 46 | 15 | 56 | 17 | 82 | 30 | 95 | 90 |

--------------------------------------------


然後再“對摺”,gap= gap / 2 = 1 這樣就只有1組。(0,1,2,3,4,5,6,7) 再進行插入排序。


  0     1     2     3     4     5     6     7    

--------------------------------------------

| 15 | 17 | 30 | 46 | 56 | 82 | 90 | 95 |

--------------------------------------------


然後再對摺  gap = gap / 2 = 0 結束。



-------------------------code-begin-------------------------

       for(gap = n / 2 ; gap > 0 ; gap /= 2) { // count of split


           for(int i = 0 ; i < gap ; i++) { // group size


               for(int j = i + gap ; j < n ; j += gap) {


                   if(rawArr[j] < rawArr[ j- gap ]) {


                       int insertVal = rawArr[j] ;


                       int k = j - gap ;


                       while(k >= 0 && rawArr[k] > insertVal) {


                           rawArr[k + gap] = rawArr[k] ;


                           k -= gap ;


                       }


                          rawArr[k + gap] = insertVal ;


                   }


               }


           }


       }


-------------------------code-end---------------------------