十大排序算法原理和代碼示例(java)

package com.pamc.sort;

import java.util.Arrays;


public class SortTest {

    static int[] arrays = {3, 5, 15, 2, 26, 4, 19, 27, 36, 44, 38, 47, 48, 50};

    static int[] arrays2 = {8, 9, 1, 6, 7, 3, 0, 5, 2, 4};

    static int[] arrays3 = {1,2,5,4,3};

    /**
     * 冒泡排序
     * 算法: 1 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
     * 2 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數;
     * 3 針對所有的元素重複以上的步驟,除了最後一個;
     * 4 重複步驟1~3,直到排序完成。
     * <p>
     * 穩定性: 穩定
     * 時間複雜度:最佳情況:T(n) = O(n)   最差情況:T(n) = O(n2)   平均情況:T(n) = O(n2)
     * 空間複雜度:0(1)
     *
     * @param arrays
     * @return
     */
    public static int[] bubbleSort(int[] arrays) {
        if (arrays == null || arrays.length < 2) {
            return arrays;
        }

        for (int i = 0; i < arrays.length; i++) {
            //循環次數,1 內循環比外循環少一次  2 每次內循環結束,冒泡出去的i元素都是最大/最小值,不需要在比較交換。
            for (int j = 0; j < arrays.length - 1 - i; j++) {
                int a = arrays[j];
                int b = arrays[j + 1];
                if (a < b) {
                    int c = a;
                    arrays[j] = b;
                    arrays[j + 1] = c;
                }
            }
        }
        return arrays;
    }

    /**
     * 選擇排序
     * 初始狀態:1 無序區爲R[1..n],有序區爲空;
     * 2 第i趟排序(i=1,2,3…n-1)開始時,當前有序區和無序區分別爲R[1..i-1]和R(i..n)。
     * 3 該趟排序從當前無序區中-選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變爲記錄個數增加1個的新有序區和記錄個數減少1個的新無序區;
     * 4  n-1趟結束,數組有序化了。
     * 簡單描述:數組長度n,從數組第1個元素開始不斷和後面的元素進行比較,找出最小記錄進行替換。直到n-1次循環比較結束
     * <p>
     * 穩定性: 不穩定
     * 時間複雜度:最佳情況:T(n) = O(n^2)  最差情況:T(n) = O(n^2)  平均情況:T(n) = O(n^2)
     * 空間複雜度:0(1)
     *
     * @param arrays
     * @return
     */
    public static int[] selectionSort(int[] arrays) {
        if (arrays == null || arrays.length < 2) {
            return arrays;
        }

        for (int i = 0; i < arrays.length; i++) {
            int index = i;
            for (int j = i + 1; j < arrays.length; j++) {
                if (arrays[j] < arrays[i]) {
                    index = j;
                }
            }
            //不能直接arrays[i] - arrays[index],有可能i = index。
            int a = arrays[i];
            int b = arrays[index];
            a = a + b;
            arrays[i] = a - b;
            arrays[index] = a - b;
        }
        return arrays;
    }

    /**
     * 插入排序
     * <p>
     * 算法: 1 從第一個元素開始,該元素可以認爲已經被排序;
     * 2 取出下一個元素,在已經排序的元素序列中從後向前掃描。如果該元素(已排序)大於新元素,將該元素移到下一位置;
     * 3 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;將新元素插入到該位置後;
     * 4 重複步驟2~5。
     * 和選擇排序很像,不過是和前面的元素比較,然後插入到正確的位置。正確的位置:大於該位置前的元素,小於等於該位置後的元素。
     * <p>
     * 穩定性:穩定
     * 時間最佳情況:T(n) = O(n)   最壞情況:T(n) = O(n^2)   平均情況:T(n) = O(n^2)
     * 空間複雜度:0(1)
     *
     * @param arrays
     * @return
     */
    public static int[] insertionSort(int[] arrays) {
        if (arrays == null || arrays.length < 2) {
            return arrays;
        }

        for (int i = 0; i < arrays.length - 1; i++) {
            int current = arrays[i + 1];
            int preIndex = i;
            while (preIndex > 0 && current < arrays[preIndex]) {
                arrays[preIndex + 1] = arrays[preIndex];
                preIndex--;
            }
            arrays[preIndex + 1] = current;
        }
        return arrays;

    }


    /**
     * 希爾排序
     * 希爾排序是把記錄按下表的一定增量(gap)分組,
     * 對每組使用直接插入排序算法排序;隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止
     * <p>
     * 穩定性: 不穩定
     * 時間複雜度最佳情況:T(n) = O(nlog^2 n)  最壞情況:T(n) = O(nlog^2 n)  平均情況:T(n) =O(nlog^2n)
     * 空間複雜度:0(1)
     *
     * @param array
     * @return
     */
    public static int[] hillSort(int[] array) {
        int len = array.length;
        int temp, gap = len / 2;
        printArrays(array);
        while (gap > 0) {
            System.out.println("-------------");
            for (int i = gap; i < len; i++) {
                // 1 根據gap分組,只需要一次循環
                // 2 preIndex是從0到len-gap-1
                // 3 i 從 gap到len-1;  preIndex:0 i:gap; preIndex:1 i:gap+1;  ...... ; preIndex:len-gap-1 i:len-1
                // 4 所以每個array[i]和array[preIndex]間隔gap,也就是array[i]和array[preIndex]一定是按照gap進行分組的同一分組內的數據
                temp = array[i];
                int preIndex = i - gap;

                System.out.println("i:" + i);
                System.out.println("preindex:" + preIndex);
                System.out.println("grap:" + gap);

                while (preIndex >= 0 && array[preIndex] > temp) {
                    array[preIndex + gap] = array[preIndex];
                    preIndex -= gap;
                }
                array[preIndex + gap] = temp;
                printArrays(array);
            }
            gap /= 2;
        }
        return array;
    }

    /**
     * 合併排序
     * 算法: 1 把長度爲n的輸入序列分成兩個長度爲n/2的子序列;
     * 2 對這兩個子序列分別採用歸併排序;
     * 3 不斷執行1,2步驟,直到子序列長度 n<2。將兩個排序好的子序列合併成一個最終的排序序列。
     * <p>
     * 穩定性: 不穩定
     * 時間複雜度:最佳情況:T(n) = O(n)  最差情況:T(n) = O(nlogn)  平均情況:T(n) = O(nlogn)
     * 空間複雜度: 0(n)
     *
     * @param arrays
     * @return
     */
    public static int[] mergeSort(int[] arrays) {
        if (arrays == null || arrays.length < 2) {
            return arrays;
        }
        int mid = arrays.length / 2;

        int[] a = Arrays.copyOfRange(arrays, 0, mid);
        int[] b = Arrays.copyOfRange(arrays, mid, arrays.length);
        return merge(mergeSort(a), mergeSort(b));
    }

    /**
     * 兩個有序數組合並,a,b數組一定是已經有序的。
     *
     * @param a
     * @param b
     * @return
     */
    protected static int[] merge(int[] a, int[] b) {
        int[] mergeArrays = new int[a.length + b.length];
        for (int index = 0, ai = 0, bi = 0; index < mergeArrays.length; index++) {
            //升序 ai判斷在前面,降序 bi放前面,判斷用>=不用>
            if (ai >= a.length) {
                // ai數組已經都比較完了,出現在b.length>a.length,這時候把b的剩餘內容有序的賦值給mergeArrays。
                mergeArrays[index] = b[bi++];
            } else if (bi >= b.length) {
                // bi數組已經都比較完了,出現在a.length>b.length,這時候把a的剩餘內容有序的賦值給mergeArrays。
                mergeArrays[index] = a[ai++];
            } else if (a[ai] > b[bi]) {
                mergeArrays[index] = b[bi++];
            } else {
                mergeArrays[index] = a[ai++];
            }
        }
        return mergeArrays;
    }

    /**
     * 快速排序方法
     *
     *  算法: 1 從數列中挑出一個元素,稱爲 “基準”(pivot),一般取數組大小範圍的隨機數;
     *       2  定義一個分區中間數smallIndex
     *       3 重新排序數列,所有元素比基準值小的擺放在smallIndex前面,所有元素比基準值大的擺在smallIndex的後面(相同的數可以到任一邊)。在這個分區退出之後,
     *         a[smallIndex]前面的數一定小於或等於a[smallIndex],a[smallIndex]後面的數一定大於a[smallIndex],
     *         這時a[smallIndex]的排序位置就是確定的。這個稱爲分區(partition)操作;
     *       4 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
     *       5 當數組的start == end 時,即遞歸到最後一個數,退出遞歸。這時smallIndex == start == end
     *
     * @param array
     * @param start
     * @param end
     * @return
     */
    public static int[] QuickSort(int[] array, int start, int end) {
        System.out.println("QuickSort  "+"start:"+start+" end:"+end);
        if (array.length < 1 || start < 0 || end >= array.length || start > end){
            return null;
        }
        int smallIndex = partition(array, start, end);
        System.out.println("返回 smallIndex:"+smallIndex);
        //當start == end 時,即遞歸到最後一個數,退出遞歸。這時smallIndex == start == end
        if (smallIndex > start){
            System.out.println("---------------");
            System.out.println("start 新的遞歸"+"start:"+start+" smallIndex:"+smallIndex);
            QuickSort(array, start, smallIndex - 1);
        }
        if (smallIndex < end){
            System.out.println("---------------");
            System.out.println("end 新的遞歸"+" end:"+end+" smallIndex:"+smallIndex);
            QuickSort(array, smallIndex + 1, end);
        }
        return array;
    }

    /**
     * 快速排序算法——partition
     *  算法: 1  選擇 取start 到 end之間的隨機數爲基數 pivot
     *       2  將a[pivot]和a[end]進行替換,方便進行比較
     *       3  將數組內小於等於a[pivot]的數放在 smallIndex 下標前面。比a[pivot]大的數放到smallIndex後面。通過兩個下標的數替換完成swap()
     *       4  排序完成後a[smallIndex]前面的數一定小於或等於a[smallIndex],a[smallIndex]後面的數一定大於a[smallIndex]。
     *          這樣a[smallIndex]的排序位置就固定了。
     * @param array
     * @param start
     * @param end
     * @return
     */
    protected static int partition(int[] array, int start, int end) {
        // 基數,取start 到 end之間的隨機數
        int pivot = (int) (start + Math.random() * (end - start + 1));
        // 大於privot的個數
        int smallIndex = start - 1;
        System.out.println("start:"+start+" end:"+end+" smallIndex:"+smallIndex+" pivot:"+pivot+" arrays:");
        printArrays(Arrays.copyOfRange(array,start,end+1));

        swap(array, pivot, end);

        System.out.println("替換:");
        printArrays(Arrays.copyOfRange(array,start,end+1));

        for (int i = start; i <= end; i++) {
            if (array[i] <= array[end]) {
                smallIndex++;
                System.out.println("i:"+i+" smallIndex:"+smallIndex);
                if (i > smallIndex) {
                    //a[i] < a[smallIndex]。當a[i] > array[end]時 a[smallIndex+1] > array[end]。
                    // 所以array[i] <= array[end]且i > smallIndex時 array[i] <= a[smallIndex]  。即將array[i]和a[smallIndex]替換
                    // smallIndex從-1開始,如果完全順序i=smallIndex,否則i > smallIndex。
                    System.out.println("arrays:");
                    swap(array, i, smallIndex);
                    printArrays(Arrays.copyOfRange(array,start,end+1));
                }
            }
        }

        return smallIndex;
    }

    /**
     * 交換數組內兩個元素
     *
     * @param array
     * @param i
     * @param j
     */
    protected static void swap(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }


    public static void printArrays(int[] arrays) {
        int i = 0;
        for (int a : arrays) {
            if (i > 0){
                System.out.print(",");
            }
            System.out.print(a);
            i++;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        printArrays(QuickSort(arrays, 0, arrays.length - 1));
    }
}

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