八大排序算法

  排序算法有內部排序和外部排序,內部排序就是數據記錄在內存中進行排序,而外部排序是因排序的數據很多,一次不能容納全部的排序數據,在排序的過程中需要訪問外存。

  這裏講述的八大排序就是內部排序。

這裏寫圖片描述

  當n較大時,則應採用時間複雜度爲o(nlog2n)的排序方法-->即快速排序,堆排序或歸併排序。

  快速排序:是當下內部排序中被認爲最好的方法,當待排序的數據是隨機分佈時,快速排序的**平均時間**最短

1、插入排序–> 直接插入排序

2、插入排序–> 希爾排序

3、選擇排序–> 簡單選擇排序

基本思想:在待排序的數組中,找出最小(或最大)的一個數與**第一個位置**上的數交換,然後在剩下的數中再找出最小(或最大)的一個數與**第二個位置**上的數交換,依次類推,直到第n-1個(倒數第二個)元素和第n個(最後一個)元素比較爲止。

簡單排序的例子:

這裏寫圖片描述

結果演示的邏輯: 
  第一趟:從n條記錄中找出最小記錄與第一條記錄交換。
  第二趟:從第二條記錄開始的n-1條記錄中找出最小記錄與第一條記錄交換。
  依次類推
  第i趟  :從第i條記錄開始的n-i條記錄開始找出最小的記錄與第i條記錄交換。
  直到所有的記錄都排好序。

由結果演示可得到簡單排序算法每一趟循環最多隻有倆個位置的記錄發生變化。這與下面要說的冒泡算法是不同的。

下面代碼是基於上述思想的一種代碼實現。自己理解不同代碼結構不同。

public static void main(){
         //定義待排序的數組 4,1, 9,2,7,5,3,6
         int a[] ={4,1,9,2,7,5,3,6};
         //定義中間變量   用於倆個數字交換時的轉換
         int tmp;
         for(int i = 0; i<a.length; i++){
             int min = i;  //  默認第i個位置的最小
             //找出從第i個位置開始的記錄中的最小記錄的索引 
             for(int j = i;j<a.length;j++){
                 if(a[j]<a[min]){
                     min = j;
                     continue;
                 }
             }

             //如果找到的最小索引與i不相等交換位置
             if(min!=i){
                 tmp = a[min];
                 a[min] = a[i];
                 a[i] = tmp;
             }

          }
         System.out.println(Arrays.toString(a));

     }

簡單排序算法的改進–>二元選擇排序

簡單的選擇排序,每趟循環只能確定一條記錄排序後的位置。我們可以考慮改進成每趟循環確定倆條記錄(當前趟的最大和最小記錄)的位置,來達到減少循環次數的目的。改進後對n條數據進行排序,最多只需要進行n/2次循環即可。
 代碼實現如下
 @Test
     public void test(){
         //定義待排序的數組 4,1, 9,2,7,5,3,6
         int a[] ={4,1,9,2,7,5,3,6};
         //定義中間變量   用於倆個數字交換時的轉換
         int tmp;
         for(int i = 0; i<a.length/2; i++){
             int min = i;  //  默認起始位置爲最小值的索引
             int max = i;  //默認起始位置爲最大值的索引

             //找出從第i個位置開始的記錄中的最小記錄的索引和最大記錄的索引 
             for(int j = i;j<a.length-i;j++){   //  注意這個地方要j<a.length-i, 因爲當前趟從起始端和末尾的調整好了數據的位置,下一趟循環把不需要的減掉
                 if(a[j]<a[min]){
                     min = j;
                 }
                 if(a[j]>a[max]){
                     max = j;
                 }
             }
             //如果找到的最小索引與i不相等交換位置
             if(min!=i){
                 tmp = a[min];
                 a[min] = a[i];
                 a[i] = tmp;
             }
            //如果找到的最大索引與i不相等交換位置
             if(max!=i){
                 tmp = a[max];
                 a[max] = a[a.length-1-i];
                 a[a.length-1-i] = tmp;
            }
         System.out.println(Arrays.toString(a));
     }

4、選擇排序–>堆排序

5、交換排序–>冒泡排序

 基本思想:在待排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的倆個數依次進行比較和調整,讓較大的數往下沉,較小的數往上冒。即 當相鄰的倆個數比較好發現它們的排序與排序要求相反時,就將它們互換。

上述思想每趟循環可能會導致許多數的位置發生變化,因爲相鄰的倆個數比較好發現它們的排序與排序要求相反時,就將它們互換,與簡單選擇排序不同,簡單選擇排序每趟循環最多倆個位置的數互換-->即倆個位置發生變化。 

冒泡排序的示例:對 49 38 65 97 76 13 27 49進行冒泡演示。

初始順序:49 38 65 97 76 13 27 49 按照從小到大冒泡

第一趟排序: 38 49 65 76 13 27 49 97
第一趟排序原理: 第一條記錄49與第二條記錄38比較 根據排序要求 49 下沉 38 上冒 數組變成(38 49 65 97 76 13 27 49),接着用第二條記錄49與第三條記錄65比較,根據排序要求 數組保持原樣(38 49 65 97 76 13 27 49),以此類推第。直到最後一條記錄。 第一趟排序的結果是(38 49 65 76 13 27 49 97)

第二趟排序:38 49 65 13 27 49 76 97
第三趟排序:38 49 13 27 49 65 76 97
第四趟排序:38 13 27 49 49 65 76 97

由冒泡演示的示例得:冒泡每趟循環存在多個位置上的數據的變化。

基於上述的思想的一種代碼實現。

 @Test
     public void test1(){
         //定義待排序的數組 49,38,65,97,76,13,27,49
         int a[] ={49,38,65,97,76,13,27,49};
         //定義中間變量   用於倆個數字交換時的轉換
         int tmp;
         for(int i = 0; i<a.length; i++){
             for(int j = 0;j<a.length-i-1;j++){   //j<a.length-i-1中-i可以省略,省略之後只是多比較了幾次,但是-1不能省略,如果不-1,即當j=a.length,for循環中j++會越界,因此j只能-1.使j不能取到最後一條記錄,此時j++能取到最後一條。
                 if(a[j]>a[j+1]){    //前一個元素>後一個  則大的下沉   小的上冒。
                     tmp = a[j];
                     a[j] = a[j+1];
                     a[j+1] = tmp;
                 }
             }
          }
         System.out.println(Arrays.toString(a));
     }

冒泡排序算法的改進

 傳統的冒泡排序中每一趟排序只能找到一個最大值或最小值,我們考慮利用在每一趟排序中進行正向和反向倆遍冒泡的方法一次可以得到倆個最終值(最大值和最小值),從而使排序趟數減少了一半。

改進後的算法的代碼實現。
@Test
     public void testMaoPao(){
        //定義待排序的數組 49,38,65,97,76,13,27,49
         int a[] ={49,38,65,97,76,13,27,49};
        //定義中間變量   用於倆個數字交換時的轉換
         int tmp;
         for(int i = 0; i<a.length/2; i++){
             //正向循環 把最大這下沉到 j+1的位置
             for(int j = i;j<a.length-i-1; j++){
                 //前一個數大於後一個   下沉
                 if(a[j]>a[j+1]){
                     tmp = a[j];
                     a[j] = a[j+1];
                     a[j+1] = tmp;
                 }
             }
             //反向循環 把最小者上冒的i的位置。
             for(int j = a.length;j>i;j--){
                 // 後一個數比前一個數小   上冒 
                 if(a[j]<a[j-1]){
                     tmp = a[j];
                     a[j] = a[j-1];
                     a[j-1] = tmp;
                 }
             }
         }
         System.out.println(Arrays.toString(a));

     }

交換排序—>快速排序

      基本思想:
      (1):選擇一個基準元素,通常選擇第一個元素或者最後一個元素。
      (2):通過一趟排序將待排序的記錄分割成獨立的兩部分,其中一部分記錄的元素值均比基準元素值小,另一部門記錄的值比基準值大。
      (3):此時基準元素在其排好序後的位置的正確位置。
      (4):然後分別對這倆部分記錄用同樣的方法繼續進行排序,整個排序過程可以遞歸進行,直到整個序列有序。

        快速排序的示例:
       
        假設用戶要排序的數組爲   49  38  65  97  76  13  27  49 。
       
        基準數:一般我爲第一個數   即 49  記爲key=49。
       
        算法實現的代碼實現思想:從數組的倆端交替的向中間掃描。
       
        方法其實很簡單:分別從初始序列“49  38  65  97  76  13  27  49
”兩端開始“探測”。先從右往左找一個小於key(49)的數,交換找到的數與key的位置,再從左往右找一個大於6的數,交換找到的數與key(本次key與上行的key的位置是不同的,因爲上行的key已經做了交換)的位置。這裏可以用兩個變量i和j,分別指向序列最左邊和最右邊。我們爲這兩個變量起個好聽的名字“哨兵i”和“哨兵j”。剛開始的時候讓哨兵i指向序列的最左邊(即i=0),指向數字49。讓哨兵j指向序列的最右邊(即=49),指向數字49。
這裏寫圖片描述

      首先哨兵j開始出動。因爲此處設置的基準數是最左邊的數,所以需要讓哨兵j先出動,這一點非常重要(請自己想一想爲什麼)。哨兵j一步一步地向左挪動(即j–),直到找到一個小於49的數(27),然後交換找到的數與基準數的位置,此時數組變成(27  38  65  97  76  13  49  49)。接下來哨兵i再一步一步向右挪動(即i++),直到找到一個數大於49的數(65),然後交換找到的數與基準算的位置。此時數組變爲(27  38  49  97  76  13  65  49),左右兩邊交替循環直到i和j相等爲止。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章