1. 程式人生 > >常用排序演算法總結(Java實現)

常用排序演算法總結(Java實現)

排序演算法比較:

在這裡插入圖片描述

1. 氣泡排序
/**
 * 氣泡排序
 * 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
 * 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
 * 針對所有的元素重複以上的步驟,除了最後一個。
 * 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
 *
 * @param array 需要排序的整型陣列
 */
public static void bubbleSort(int[] array) {
    int temp = 0;
    int len = array.
length; for (int j = 0; j < len - 1; j++) { for (int i = 0; i < len - 1 - j; i++) { if (array[i] > array[i + 1]) { temp = array[i]; array[i] = array[i + 1]; array[i + 1] = temp; } } } }
氣泡排序的改進:雞尾酒排序
/**
 * 雞尾酒排序(氣泡排序的改進)
 * 先找到最小的數字,把他放到第一位,然後找到最大的數字放到最後一位。
 * 再找到第二小的數字放到第二位,再找到第二大的數字放到倒數第二位。
 * 以此類推,直到完成排序。
 *
 * @param array 需要排序的整型陣列
 */
public static void cocktailSort(int[] array) {
    int temp = 0;
    int len = array.length;

    for (int j = 0; j < len / 2; j++) {
        //陣列中最大的數向右冒泡
        for
(int i = j; i < len - 1 - j; i++) { if (array[i] > array[i + 1]) { temp = array[i]; array[i] = array[i + 1]; array[i + 1] = temp; } } //陣列中最小的數向左冒泡 for (int i = len - 1 - (j + 1); i > j; i--) { if (array[i] > array[i + 1]) { temp = array[i]; array[i] = array[i + 1]; array[i + 1] = temp; } } } }
2. 快速排序
/**
 * 查找出中軸(預設是最低位start)的在陣列指定區間排序後所在位置
 *
 * @param array 待查詢陣列
 * @param start 開始位置
 * @param end 結束位置
 * @return 中軸所在位置
 */
public static int partition(int[] array, int start, int end) {
    int key = array[start]; //中軸通常設定為序列的第一項

    while (start < end) {
        while (start < end && array[end] >= key)
            end--;
        array[start] = array[end];
        while (start < end && array[start] <= key)
            start++;
        array[end] = array[start];
    }
    array[start] = key;

    return start;
}

/**
 * 遞迴實現的快速排序
 * 典型的分而治之思想
 *
 * @param array 待排序陣列
 * @param start 開始位置
 * @param end 結束位置
 */
public static void quickSort(int[] array, int start, int end) {
    if (start < end) {
        int index = partition(array, start, end);
        quickSort(array, start, index - 1);
        quickSort(array, index + 1, end);
    }
}
3. 選擇排序
/**
 * 選擇排序演算法
 * 在未排序序列中找到最小元素,存放到排序序列的起始位置
 * 再從剩餘未排序元素中繼續尋找最小元素,然後放到排序序列末尾。
 * 以此類推,直到所有元素均排序完畢。
 *
 * @param array 需要排序的整型陣列
 */
public static void selectSort(int array[]) {
    int len = array.length;
    int temp = 0;

    for (int i = 0; i < len - 1; i++) { //i為已排序序列的末尾
        int min = i;
        //找出未排序序列最小值所在的位置
        for (int j = i + 1; j < len; j++) {
            if (array[j] < array[min])
                min = j;
        }
        temp = array[i];
        array[i] = array[min];
        array[min] = temp;
    }
}
4. 插入排序
/**
 * 插入排序
 * 
 * 類似抓撲克牌排序
 * 從第一個元素開始,該元素可以認為已經被排序
 * 取出下一個元素,在已經排序的元素序列中從後向前掃描
 * 如果該元素(已排序)大於新元素,將該元素移到下一位置
 * 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
 * 將新元素插入到該位置中
 * 重複步驟2
 *
 * @param array 待排序陣列
 */
public static void insertSort(int[] array) {
    int len = array.length;
    int j, temp = 0;

    for (int i = 1; i < len; i++) {
        temp = array[i];
        for (j = i; j > 0 && temp < array[j - 1]; j--) {
            array[j] = array[j - 1];
        }
        array[j] = temp;
    }
}
插入排序的改進:希爾排序
/**
 * 希爾排序(插入排序的高效改進)
 * 
 * 將整個有序序列分割成若干小的子序列分別進行插入排序。
 *
 * @param array 待排序陣列
 */
public static void shellSort(int[] array) {
    int len = array.length;
    int j, temp = 0;

    int increment = len; //設定初始步長
    while (true) {
        increment /= 2; //每次將步長縮短為原來的一半
        for (int x = 0; x < increment; x++) {
            for (int i = x + increment; i < len; i += increment) {
                temp = array[i];
                for (j = i - increment; j >= 0 && array[j] > temp; j -= increment) {
                    array[j + increment] = array[j];
                }
                array[j + increment] = temp;
            }
        }
        if (increment == 1)
            break;
    }
}
5.歸併排序
/**
 * 歸併排序
 *
 * 將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。
 * 然後再把有序子序列合併為整體有序序列。
 *
 * 時間複雜度為O(nlogn)
 * 穩定排序方式
 *
 * @param array 待排序陣列
 */
public static void mergeSort(int[] array, int start, int end) {
    int mid = (start + end) / 2;

    if (start < end) {
        mergeSort(array, start, mid);
        mergeSort(array, mid + 1, end);
        merge(array, start, mid, end);
    }
}

/**
 * 將陣列中low到high位置的數進行排序
 *
 * @param array 待排序陣列
 * @param start 待排的開始位置
 * @param mid   待排中間位置
 * @param end   待排結束位置
 */
public static void merge(int[] array, int start, int mid, int end) {
    int[] temp = new int[end - start + 1]; //輔助陣列
    int i = start; //前一陣列的起始元素
    int j = mid + 1; //後一陣列的起始元素
    int k = 0;

    //把較小的數先移到新陣列中
    while (i <= mid && j <= end) {
        if (array[i] <= array[j]) { //帶等號保證歸併排序的穩定性
            temp[k++] = array[i++];
        } else {
            temp[k++] = array[j++];
        }
    }

    //把左邊剩餘的數移入陣列
    while (i <= mid) {
        temp[k++] = array[i++];
    }

    //把右邊邊剩餘的數移入陣列
    while (j <= end) {
        temp[k++] = array[j++];
    }

    //把新陣列中的數覆蓋array陣列
    for (k = 0; k < temp.length; k++) {
        array[start + k] = temp[k];
    }
}
6. 堆排序
/**
 * 堆排序
 *
 * 由輸入的無序陣列構造一個最大堆,作為初始的無序區
 * 把堆頂元素(最大值)和堆尾元素互換
 * 把堆(無序區)的尺寸縮小1,並從新的堆頂元素開始進行堆調整
 * 重複步驟2,直到堆的尺寸為1
 *
 * 時間複雜度為O(nlogn)
 * 不穩定排序方式
 *
 * @param array 待排序陣列
 */
public static void heapSort(int[] array) {
    int temp;

    //構建大頂堆
    for (int i = (array.length / 2) - 1; i >= 0; i--) { //從第一個非葉子結點從下至上,從右至左調整結構
        adjustHeap(array, i, array.length);
    }

    //交換堆頂元素與末尾元素,並重新調整堆結構
    for (int j = array.length - 1; j > 0; j--) {
        temp = array[0];
        array[0] = array[j];
        array[j] = temp;

        adjustHeap(array, 0, j);
    }
}

/**
 * 調整大頂堆
 *
 * @param array
 * @param i
 * @param length
 */
public static void adjustHeap(int[] array, int i, int length) {
    int temp = array[i]; //取出當前元素值暫存

    for (int k = i * 2 + 1; k < length; k = k * 2 + 1) { //從i結點的左子結點開始
        if (k + 1 < length && array[k] < array[k + 1]) { //如果左子結點小於右子結點,k指向右子結點
            k++;
        }

        if (array[k] > temp) { //如果子節點大於起始元素,則將子節點值賦給父節點
            array[i] = array[k];
            i = k;
        } else {
            break;
        }
    }
    array[i] = temp; //將起始元素值放到最終的位置
}
思考:

Java系統提供的Arrays.sort函式。對於基礎型別,底層使用快速排序。對於非基礎型別,底層使用歸併排序。請問是為什麼?

答:這是考慮到排序演算法的穩定性。對於基礎型別,相同值是無差別的,排序前後相同值的相對位置並不重要,所以選擇更為高效的快速排序,儘管它是不穩定的排序演算法;而對於非基礎型別,排序前後相等例項的相對位置不宜改變,所以選擇穩定的歸併排序。