1. 程式人生 > 實用技巧 >八大排序演算法-初學筆記

八大排序演算法-初學筆記

八大經典排序    1、氣泡排序 基本介紹:氣泡排序是一種交換排序 基本思想:(假定從小到大的順序)從第一個元素開始,相鄰兩個資料進行比較,將小的值放在左邊。第一輪:從第一個元素開始,和與其相鄰的後一個元素進行比較,         若後一個比前一個小,則交換位置,否則不變,然後兩個索引都向後移動一位,繼續判斷至最後一組資料,第二輪:從第二個元素出發,繼續重複上述進行比較、換位,         再進行下一輪比較,直到最後一輪結束。                  時間複雜度:O(n2),通過測試,長度為80000的陣列,陣列中的元素從0-800000隨機賦值,執行時間為6s; 程式碼實現:
public
static void selectX(int[] arr){ for (int i = 0; i < arr.length - 1; i++) { for (int j = i + 1; j < arr.length; j++) { if(arr[i] > arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } }

2、簡單選擇排序 基本介紹: 選擇排序也屬於內部排序;與堆排序共為選擇排序 基本思想: (也是假定從小到大的順序) 先設定第一個元素為最小,讓其後邊的每個元素都與其判斷,儲存最小的資料和最小資料的索引位置,         在一輪判斷後將最小值與第一個元素做替換,再設定第二個元素為當前最小值,繼續判斷,直到最後一組結束。          時間複雜度:O(n2),通過測試,長度為80000的陣列,陣列中的元素從0-800000隨機賦值,執行時間不到1s; 程式碼實現:
 public static void select2(int[] arr) {    //   80000 :  不到一秒    
// 800000 : 一分鐘 int minIndex = 0; int min = arr[minIndex]; for(int i = 0; i < arr.length - 1; i++) { for(int j = i + 1; j < arr.length;j++) { if(arr[j] < min) { min = arr[j]; minIndex = j; } } if(minIndex != i) { arr[minIndex] = arr[i]; arr[i] = min; } } }

3、簡單插入排序 基本介紹:插入排序也屬於內部排序;與希爾排序同為插入排序 基本思路:(小->大)先假定有兩個陣列,無序陣列和有序陣列,將無序陣列中的一個元素與有序中的元素一一比較,找出合適的位置後插入進去。               時間複雜度: O(n2),通過測試,長度為80000的陣列,陣列中的元素從0-800000隨機賦值,執行時間不到1s; 程式碼實現 :
 public static void insert(int[] arr) {        
             for(int i = 1; i < arr.length ; i++) {
                    int insertIndex = i - 1;
                    int insertVal =  arr[i];
                    while(insertIndex >= 0 && insertVal < arr[insertIndex]) {   //找插入的地方
                           arr[insertIndex + 1] = arr[insertIndex]; //逐個往後移一位,
                           insertIndex--;
                    }
                    arr[insertIndex + 1] = insertVal; //找到了小的數放的位置
             }
       }

4、希爾排序 (插入排序)   基本介紹:希爾排序是插入排序的一種。 基本思路: 希爾排序是將一個數組先分成長度/2組資料,同一組資料相鄰間距為長度/2,根據交換法或位移法將資料進行交換,交換完後再將資料分為間距/2組資料,         每一組之間繼續進行比較交換,直到間距為0,即為結束。                  時間複雜度:O(n2) 通過測試, 交換法 8萬資料 4s; 位移法 80萬資料 不到1s 800萬資料2s, 顯然希爾位移法排序效率更高。 程式碼實現:
  //交換法
       public static void shellSort(int[] arr) {   // 八萬 : 4s
             int count = 0;
             int temp = 0;
             for(int gsp = arr.length / 2; gsp > 0; gsp /= 2) {
                    for(int i = gsp; i < arr.length; i++) {
                           for(int j = i - gsp; j > 0; j -= gsp) {
                                 if(arr[j] > arr[j + gsp]) {
                                        temp = arr[j];
                                        arr[j] = arr[j + gsp];
                                        arr[j + gsp] = temp;
                                 }
                           }
                    }
             }
       }
    //移位法
       public static void shellSort2(int[] arr) {   // 8000000  兩秒
             for(int gap = arr.length / 2; gap > 0; gap /= 2) {
                    for(int i = gap; i < arr.length; i++) {
                           int j = i;
                           int temp = arr[j];
                           while(j - gap > 0 && temp < arr[j - gap]) {
                                 arr[j] = arr[j - gap];
                                 j -= gap;
                           }
                           arr[j] = temp;
                    }
             }
       }

5、快速排序 基本介紹:快速排序是對氣泡排序的一種改進。 基本思想:通過一趟排序,將資料分為以某個值大小為界限的兩部分,再將這兩部分值分別以以上方式繼續分組,直到資料變為有序。此方法可以用遞迴呼叫,用空間換時間,    大大提高了排序所耗費的時間。        時間複雜度:O(n*logn)80萬資料 不到1s 800萬資料2s 程式碼實現:
 public static void quick(int[] arr,int lift,int right) {
             
             if(lift < right) {
                    int l = lift;
                    int r = right;
                    int key = arr[l];
                    while(l < r) {
                           while(l < r && arr[r] >= key) {
                                 r -= 1;
                           }
                           arr[l] = arr[r];
                           
                           while(l < r && arr[l] <= key) {
                                 l += 1;
                           }
                           arr[r] = arr[l];
                            //這裡可能已經完成,因此賦值需要移位**
                           if(l < r) {
                                 arr[r--] = arr[l];
                           }
                    }
                    arr[l] = key;
                    //左向遞迴
                    quick(arr,lift,l - 1);
                    //右向遞迴
                    quick(arr,l + 1,right);
             }
       }

6、歸併排序 基本介紹:歸併排序用到了分治策略,先將陣列分解成一個個小的陣列,然後再將陣列進行排序整合,變成一個有序的大陣列。 基本思想:用遞迴的方法將一個長的陣列分解成一個個小的陣列,然後給每個分解過的陣列再進行排序操作,直到完成排序。        時間複雜度:O(nlog2) 800萬個資料只用了不到一秒的時間 程式碼實現:
 //分: 提供分解陣列的方法
       public static void merge(int[] arr, int left, int right,int[] temp) {
             if(left < right) {
                    int mid = (left + right) / 2;
                    //左分
                    merge(arr, left, mid, temp);
                    //右分
                    merge(arr, mid + 1, right, temp);
                    
                    mergeSort(arr,left,right,mid,temp);
             }
       }
       //治: 提供合數組的方法
       public static void mergeSort(int arr[], int left, int right, int mid, int temp[]) {
             if(left < right) {
                    int i = left;
                    int j = mid + 1;
                    //temp臨時的索引
                    int t = 0;
                    //三大步驟進行合併
                    //1.先將兩個部分的陣列比較後放入臨時陣列中,直到其中一個數組放完
                    while(i <= mid && j <= right) {
                           if(arr[i] <= arr[j]) {
                                 temp[t] = arr[i];
                                 i += 1;
                                 t += 1;
                           }else {
                                 temp[t] = arr[j];
                                 j += 1;
                                 t += 1;
                           }
                    }
                    //2.再將另一個沒放完的陣列全部放入臨時陣列中
                    while(i <= mid) {
                           temp[t] = arr[i];
                           t += 1;
                           i += 1;
                    }
                    while(j <= right) {
                           temp[t] = arr[j];
                           j += 1;
                           t += 1;
                    }
                    //3.將臨時陣列放入原陣列中
                    int index = left;
                    int k = 0;
                    while(k < t) {
                           arr[index] = temp[k];
                           k += 1;
                           index += 1;
                    }
             }
       }

7、基數排序 基本介紹:基數排序用到了桶排序的思想,將資料的同一位進行比較,然後放入對應的桶中,再逐個取出資料。 基本思想:先建立十個桶,每個桶代表著一個數位上的數字,將所有陣列資料長度設定為最大數位的值,在陣列中取出同一個數位上的數字進行入桶操作,然後再逐個取出來,        分別從低位到高位進行入桶、出桶,最終就會得到有序的陣列。          時間複雜度:O(n*logn) 800萬 不到1s 程式碼實現:
 public static void radixSort(int[] arr) {
             
             //獲取最大數的位數
             int max = 0;
             for(int i = 0;i < arr.length; i++) {
                    if(max < arr[i]) {
                           max = arr[i];
                    }
             }
             int maxLength = (max + "").length();
             //建立桶子  二維陣列
             int [][] tong =new int[10][arr.length];
             //建立每個桶子的數的個數
             int[] inNumber = new int[10];
             //執行操作
             for(int i = 0,n = 1; i < maxLength; i++,n*=10) {
                    
                    //入桶,求出每個元素的位值,然後入桶
                    for(int j = 0 ; j < arr.length; j++) {
                           int digOfElement = (arr[j] / n) % 10;
                           tong[digOfElement][inNumber[digOfElement]] = arr[j];
                           inNumber[digOfElement]++;//忘了移動索引
                    }
                    int index = 0;
                    //出桶
                    for(int k = 0; k < inNumber.length; k++) {
                           if(inNumber[k] != 0) {
                                 //放值
                                 for(int l = 0; l < inNumber[k]; l++) {
                                        arr[index++] = tong[k][l];
                                 }                   
                           }
                           inNumber[k] = 0;
                    }      
             }
     

8、堆排序

   基本介紹:堆排序用到的是線性二叉陣列,也屬於選擇排序的一種。

   基本思想:先將陣列調整成為一個大頂堆,然後將頂元素與最後一個元素位置交換,陣列長度減一。將交換後的陣列繼續調整成為新的大頂堆,並再次進行交換,以此類推。

                

   時間複雜度: O(n*logn) 800萬資料不到 1s完成。

   程式碼實現:

//編寫一個堆排序
    public static void heapSort(int[] arr) {
        System.out.println("堆排序!!");
        int temp = 0;
        //最終程式碼:
        //1.先弄一個大頂堆,升序
        for(int i = arr.length / 2 + 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        //2,交換大頂堆和最後一個元素,長度減一
        //3,在剩下的陣列中調整大頂堆。
        for(int j = arr.length - 1; j > 0; j--) {
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, j);
        }   
    }
    
    //將一個數組(二叉樹),調整成一個大頂堆
    /**
     * 
     * @param arr[]  待調整的陣列
     * @param i      表示非葉子節點再陣列中的索引
     * @param length    表示當前陣列中還有多少待調整的元素
     */    
    public static void adjustHeap(int arr[], int i, int length) {
        //先取出當前元素的值
        int temp = arr[i];
        //開始調整
        for(int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            //從左右子節點中找到最大的那個值的索引
            if(k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            //如果子節點大於父節點,直接交換
            if(arr[k] > temp) {
                arr[i] = arr[k];
                i = k;//i 指向最大的元素k,繼續迴圈比較
            }else {
                break;//我們是從左到右,從下到上,不必判斷左下層子節點
            }
        }
        //當for迴圈結束後,已經將以i為父節點的區域性的樹變成大頂堆。
        arr[i] = temp;//將temp值放到調整後的位置。 
    }

相關術語解釋:   1、穩定:如果a原先再b前面,而a=b,排序之後a仍然在b的前面;     不穩定:如果a原先再b前面,而a=b,排序之後a可能會出現在b的後面;   2、內排序:所有排序操作都在記憶體中進行     外排序:由於資料太大,因此把資料放在磁碟中,而排序通過磁碟和記憶體的資料傳輸才能進行   3、時間複雜度:一個演算法執行所耗費的時間。   4、空間複雜度:執行一個程式所需記憶體的大小。   5、n: 資料規模;k:“桶的個數”。   6、In-place: 不佔用額外記憶體;     Out-place:佔用額外記憶體。