天天看點

資料結構與算法系列 -- 歸并排序

       歸并排序的核心思想還是蠻簡單的。如果要排序一個數組,我們先把數組從中間分成前後兩 部分,然後對前後兩部分分别排序,再将排好序的兩部分合并在一起,這樣整個數組就都有 序了。  如下圖,針對數組:11、   8、   3、  9、7、1、2、5,歸并排序的過程如下                                                                

資料結構與算法系列 -- 歸并排序

僞代碼如下:  

void test(){

 merge_sort(data,0,data.length-1)

}



//對  數組data 下标為 r 到 q 進行排序
void  merge_sort(data,r,q){
   if(r>=q)
        return;

  int mid = (r + q)/2
  merge_sort(data,r,mid)
  merge_sort(data,mid+1,q)
  merge(data,r,mid,mid+1,q)



}

//将已經有序的 data[r1] ... data[q1] ,  data[r2] ... data[q2] ,合并排序為有序數組
void  merge(data,r1,q1,r2,q2){


}
           

  Java代碼如下:

public class SortTest {

    private void swap(int[] data,int i,int j){
           int tmp = data[i];
           data[i] = data[j];
           data[j] = tmp;
    }

    public void printArray(int[] data){
        for(int d:data){
            System.out.print(d+" ");
        }
        System.out.println();
    }

  
    /**
     * 歸并排序
     */
    public void mergeSort(int[] data,int r1,int q1,int r2,int q2){
        int start1 = r1;
        int start2 = r2;
        int start  = 0;
        int[] tmp = new int[q2-r1+1];
        while (start1<=q1 && start2 <=q2){
            if(data[start1] <= data[start2]){
                tmp[start++] = data[start1++];
            }else {
                tmp[start++] = data[start2++];
            }
        }

        while (start1<=q1){
             tmp[start++] = data[start1++];
        }

        while (start2<=q2){
            tmp[start++] = data[start2++];
        }

        for(int i=r1;i<=q2;i++){
            data[i] = tmp[i-r1];
        }

    }

    public void merge(int[] data,int r,int q){
        if(r>=q)
            return;

        int middle = (r+q)/2;
        merge(data,r,middle);
        merge(data,middle+1,q);
        mergeSort(data,r,middle,middle+1,q);

    }
    /**
     * 歸并排序
     */
    @Test
    public void test4(){
        int[]  data = {55,76,34,66,22,31,13,25,90,87,56,65,66};
        //int[]  data = {6,7,8,9,1,2,30,40,50};
        //mergeSort(data,0,3,4,8);
        merge(data,0,data.length-1);
        printArray(data);

    }
}
           

結果:

13 22 25 31 34 55 56 65 66 66 76 87 90 
           

分析:

第一,歸并排序是穩定的排序算法嗎? 歸并排序穩不穩定關鍵要看 merge() 函數,也就是兩個有序子數組合并成一個有序數組的那部分代碼。 我們可以控制該函數在合并時,保證相同值元素 的先後順序不變。是以,歸并排序是一個穩定的排序算法。   第二,歸并排序的時間複雜度是多少? 從我們的原理分析和僞代碼可以看出,歸并排序的執行效率與要排序的原始數組的有序程度 無關,是以其時間複雜度是非常穩定的,不管是最好情況、最壞情況,還是平均情況,時間 複雜度都是 O(nlogn)。   第三,歸并排序的空間複雜度是多少? 歸并排序的時間複雜度任何情況下都是 O(nlogn),看起來非常優秀。(待會兒你會發現, 即便是快速排序,最壞情況下,時間複雜度也是 O(n 2 )。)但是,歸并排序并沒有像快排那 樣,應用廣泛,這是為什麼呢?因為它有一個緻命的“弱點”,那就是歸并排序不是原地排 序算法。