常見的排序算法 Java實現

最近在準備面試,回顧了一下之前學習的幾種排序算法,並參考材料嘗試實現。現在此記錄一下,以後忘了可以回顧。

直接貼上代碼(有許多值得優化的地方)。

package hpp.sort;

/**
 * Created by hpp on 2017/8/4.
 */
public class SortTest {

    public static void main(String[] args) {

        int[] array = {1,2,5,4,7,9,0,8,3,6};
//        insertSort(array,array.length);
//        insertSort2(array,array.length);
//        shellSort(array);
//        bubbleSort(array);
//        quickSort(array, 0, array.length - 1);
//        selectSort(array);
//        heapSort(array);
        mergeSort(array);

        for(int item:array){
            System.out.print(item + " ");
        }

    }

    /**
     * 直接插入排序,空間複雜度O(1),時間複雜度O(n²)
     * 適用於基本有序的排序表和數據量不大的排序表
     */
    public static void insertSort(int[] array, int n){
        if(array==null)
            return;
        int i,j,temp;
        for(i=1;i<n;i++){
            if(array[i]<array[i-1]){//減少比較移動的次數
                temp = array[i];
                j = i - 1;
                while (j>=0){
                    if (temp<array[j]) {
                        array[j + 1] = array[j];
                    } else {
                        break;
                    }
                    j--;
                }
                array[j+1] = temp;
            }
        }
    }


    /**
     * 折半插入排序,空間複雜度O(1),時間複雜度O(n²)
     * @param array
     * @param n
     */
    public static void insertSort2(int[] array, int n){

        if(array==null)
            return;;
        int i,j,temp,low,high,mid;
        for(i=1;i<n;i++){
            temp = array[i];
            low = 0;
            high = i-1;
            while (low<=high){//找到要插入的位置
                mid = (low + high)/2;
                if(array[mid]>temp){
                    high = mid - 1;
                }else {
                    low = mid + 1;
                }
            }

            for(j = i-1;j>=high+1;--j){
                array[j+1] = array[j];
            }
            array[high+1] = temp;
        }
    }


    /**
     * 希爾排序
     * @param arr
     */
    private static void shellSort(int[] arr) {

        int j;
        int len = arr.length;
        for(int val=len>>1; val>0; val>>=1) {
            //下面是對本次的所有分組做直接插入排序
            for(int i=val; i<len; i++) {
                int temp = arr[i];
				/*
				 * 爲什麼每次都用temp比較呢?
				 * 因爲直接插入就是找到temp的合適位置。
				 * 爲什麼temp<arr[j-val]這個條件可以放在for內呢?
				 * 因爲原來的組內數據已經有序,找到位置就停止便是。
				 * 不甚理解的去看直接插入排序吧。
				 */
                for(j=i; j>=val&&temp<arr[j-val]; j-=val) {
					/*
					 * 爲什麼是arr[j-val]不是arr[j]呢?
					 * 因爲j=i開始的,而且條件是j>=val&&temp<arr[j-val]
					 */
                    arr[j] = arr[j-val];
                }
				/*
				 * 注意不是arr[i] = temp
				 * 直接插入排序也是這樣的。
				 * 爲什麼呢?
				 * 因爲j是位置,i是待插入元素
				 */
                arr[j] = temp;
            }
        }
    }


    /**
     * 冒泡排序,空間複雜度O(1),時間複雜度O(n²),穩定
     * @param array
     */
    private static void bubbleSort(int[] array){
        if (array==null)
            return;
        int length = array.length-1;
        for(int i=0;i<length;i++){
            for (int j=length;j>i;j--){
                if (array[j-1]>array[j]){
                    int tmp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = tmp;
                }
            }
        }
    }


    /**
     * 快排,空間複雜度最壞O(n),平均O(logn),時間複雜度最壞情況下O(n²),平均情況下O(nlogn),不穩定
     */
    public static void quickSort(int[] array,int left,int right){
        if (array==null)
            return;
        if (left<right){
            int pivot = partition(array,left,right);
            quickSort(array,left,pivot-1);
            quickSort(array,pivot+1,right);
        }
    }
    //快排一次劃分
    private static int partition(int[] array,int left,int right){
        int tmp = array[left];
        while (left<right){
            while (left<right && array[right]>=tmp)// 從右向左找小於tmp的數來填array[left]
                right--;
            if (left<right)
                array[left++] = array[right];
            while (left<right && array[left]<tmp)// 從左向右找小於tmp的數來填array[right]
                left++;
            if (left<right)
                array[right--] = array[left];
        }
        array[left] = tmp;//此時的left相當於pivot
        return left;
    }


    /**
     * 簡單選擇排序,空間複雜度O(1),時間複雜度O(n²),不穩定
     */
    public static void selectSort(int[] array){
        if (array==null)
            return;
        int length = array.length;
        for(int i=0;i<length;i++){
            int min = i;//記錄最小元素位置
            for(int j=i+1;j<length;j++){
                if(array[j]<array[min]){//更新最小元素位置
                    min = j;
                }
            }
            if (min!=i){//把最小元素和i位置上的元素交換
                int tmp = array[i];
                array[i] = array[min];
                array[min] = tmp;
            }
        }
    }


    /**
     * 堆排序:空間複雜度O(1),時間複雜度O(nlogn),不穩定
     */


    public static void heapSort(int[] data) {

        buildMaxHeap(data);// 構建最大堆
        for (int i = data.length; i > 1; i--) {// 循環,每次把根節點和最後一個節點調換位置
            int tmp = data[0];
            data[0] = data[i - 1];
            data[i - 1] = tmp;
            maxHeapify(data, 1, i - 1);// 堆的長度減少1,排除置換到最後位置的根節點
        }
    }

    // 根據輸入數組構建一個最大堆
    private static void buildMaxHeap(int[] array) {
        for (int i = array.length / 2; i > 0; i--) {
            maxHeapify(array, i, array.length);
        }
    }

    //堆調整,使其生成最大堆
    private static void maxHeapify(int[] data, int parentNodeIndex, int heapSize) {
        // 左子節點索引
        int leftChildNodeIndex = parentNodeIndex * 2;
        // 右子節點索引
        int rightChildNodeIndex = parentNodeIndex * 2 + 1;
        // 最大節點索引
        int largestNodeIndex = parentNodeIndex;

        // 如果左子節點大於父節點,則將左子節點作爲最大節點
        if (leftChildNodeIndex <= heapSize && data[leftChildNodeIndex - 1]>data[parentNodeIndex - 1]) {
            largestNodeIndex = leftChildNodeIndex;
        }

        // 如果右子節點比最大節點還大,那麼最大節點應該是右子節點
        if (rightChildNodeIndex <= heapSize && data[rightChildNodeIndex - 1]>data[largestNodeIndex - 1]) {
            largestNodeIndex = rightChildNodeIndex;
        }

        // 最後,如果最大節點和父節點不一致,則交換他們的值
        if (largestNodeIndex != parentNodeIndex) {
            int tmp = data[parentNodeIndex - 1];
            data[parentNodeIndex - 1] = data[largestNodeIndex - 1];
            data[largestNodeIndex - 1] = tmp;
            // 交換完父節點和子節點的值,對換了值的子節點檢查是否符合最大堆的特性
            maxHeapify(data, largestNodeIndex, heapSize);
        }
    }


    /**
     * 2-路歸併排序,空間複雜度O(n),時間複雜度O(nlogn)
     */
    public static void mergeSort(int[] data) {
        sort(data, 0, data.length - 1);
    }

    public static void sort(int[] data, int left, int right) {
        if (left >= right)
            return;
        // 找出中間索引
        int center = (left + right) / 2;
        // 對左邊數組進行遞歸
        sort(data, left, center);
        // 對右邊數組進行遞歸
        sort(data, center + 1, right);
        // 合併
        merge(data, left, center, right);
    }

    /**
     * 將兩個數組進行歸併,歸併前面2個數組已有序,歸併後依然有序
     * @param data    數組對象
     * @param left    左數組的第一個元素的索引
     * @param center    左數組的最後一個元素的索引,center+1是右數組第一個元素的索引
     * @param right    右數組最後一個元素的索引
     */
    public static void merge(int[] data, int left, int center, int right) {
        // 臨時數組
        int[] tmpArr = new int[data.length];
        // 右數組第一個元素索引
        int mid = center + 1;
        // third 記錄臨時數組的索引
        int third = left;
        // 緩存左數組第一個元素的索引
        int tmp = left;
        while (left <= center && mid <= right) {
            // 從兩個數組中取出最小的放入臨時數組
            if (data[left] <= data[mid]) {
                tmpArr[third++] = data[left++];
            } else {
                tmpArr[third++] = data[mid++];
            }
        }
        // 剩餘部分依次放入臨時數組(實際上兩個while只會執行其中一個)
        while (mid <= right) {
            tmpArr[third++] = data[mid++];
        }
        while (left <= center) {
            tmpArr[third++] = data[left++];
        }
        // 將臨時數組中的內容拷貝回原數組中
        // (原left-right範圍的內容被複制回原數組)
        while (tmp <= right) {
            data[tmp] = tmpArr[tmp++];
        }
    }



}



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