我所知道的排序算法之歸併排序

做者前言

你們好,我是阿濠,今篇內容跟你們分享的是排序算法之歸併排序,很高興分享到segmentfault與你們一塊兒學習交流,初次見面請你們多多關照,一塊兒學習進步.

1、歸併排序的介紹

基本介紹

歸併排序(MERGE- SORT)是利用歸併的思想實現的排序方法,該算法採用經典的分治( divide- and-conquer)策略(分治法將問題分(divide)成一些小的問題而後遞歸求解,而治(conquer)的階段則將分的階段獲得的各答案修補」在一塊兒,即分而治之)。算法

基本思想

圖片.png

1.把數組從中間劃分紅兩個子數組;
2.一直遞歸地把子數組劃分更小的子數組直到子數組裏面只有一個元素
3.依次按照遞歸的返回順序,不斷地合併排好序的子數組,直到最後把整個數組的順序排好segmentfault

看看治階段,咱們須要將兩個已經有序的子序列合併成一個有序序列,好比上圖中的最後一次合併,要將[4,5,7,8]和[1,2,3,6]兩個已經有序的子序列,合併爲最終序列[1,2,3,4,5,6,7,8],來看下實現步驟數組

圖片.png

2、代碼實現思想

按圖所示實現分解方法

圖片.png

//int arr[]={8,4,5,7,1,3,6,2};
//int temp[]=new int[arr.length];
//分解方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
    if (left < right) {
        int mid = (left + right) / 2; //中間索引
        //向左遞歸進行分解
        //0 - mid 便是0 - 3 {8,4,5,7}
        mergeSort(arr, left, mid, temp);
        //向右遞歸進行分解
        //mid+1 - midright 便是4 - 7  {1,3,6,2}
        mergeSort(arr, mid + 1, right, temp);
    }
}

按圖所示實現合併方法

圖片.png

//合併的方法
/**
 *
 *  @param  arr 排序的原始數組
 *  @param left 左邊有序序列的初始索引
 *  @param mid 中間索引
 *  @param right 右邊索引
 *  @param temp 作中轉的數組
 **/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
    //初始化i,左邊有序序列的初始索引
    int i = left;
    //初始化j,右邊有序序列的初始索引
    //爲何要mid+1?
    //由於假設數組arr{1,3,5,6,2,4} mid=(left+right)/2 = 2
    //此時左邊i=left mid左邊的就是 0 - mid 便是{1,3,5}
    //此時右邊就是mid+1 - right 便是{6,2,4}
    int j = mid+1;

    int t = 0;//指向temp數組的當前索引

    //(-)
    //先把左右兩邊(有序)的數據按照規則填充到temp數組
    //直到左右兩邊的有序序列,有一邊處理完畢爲止
    //i <= mid 表明左邊有序序列有值
    //j <= right 表明右邊有序序列有值
    while (i <= mid & j <= right) {//繼續

        //若是左邊的有序序列的當前元素,小於等於右邊有序序列的當前元素
        //即將左邊的當前元素,拷貝到temp數組
        //假設數組arr{1,3,5,6,2,4}
        //左邊 0 - mid 便是{1,3,5}
        //右邊 mid+1 -right 便是{6,2,4}
        //若arr[i]<= arr[j] 便是1 <= 6
        if (arr[i] <= arr[j]) {
            temp[t] = arr[i];//temp[0]=arr[i];
            t += 1;//指向temp數組下一位
            i += 1;//指向左邊下一位arr[i+1]...
        }else{
            //反之arr[i] >= arr[j] 左邊大於右邊
            //則進行右邊賦值給temp數組
            temp[t] = arr[j];//temp[0]=arr[i];
            t += 1;//指向temp數組下一位
            j += 1;//指向右邊邊下一位arr[j+1]...
        }
    }

    //(二)
    //把有剩餘數據的一邊的數據依次所有填充到temp
    //左邊的有序序列還有剩餘的元素,就所有填充到temp
    while( i <= mid){
        temp[t] = arr[i];
        t += 1;
        i += 1;
    }
    //右邊的有序序列還有剩餘的元素,就所有填充到temp
    while( j <= right){
        temp[t] = arr[j];
        t += 1;
        j += 1;
    }

    //(三)
    //將temp數組的元素拷貝到arr
    //爲何 t=0 ?
    //由於合併的時候按圖所示數組:{8,4,5,7,1,3,6,2}
    //最早進入的是84 left=0 right = 1
    //通過上面的左邊與右邊比較,得出temp數組:4,8
    // 此時清空指向temp數組的下標指針t 從新回到0
    //tempLeft = 0 進行將temp數組裏的4,8 賦值給arr數組
    t = 0;
    int tempLeft= left;
    while( tempLeft <= right){
        arr[tempLeft]=temp[t];
        t += 1;//賦值成功後指向temp數組的下標指針t日後移
        tempLeft +=1;//84 完成後到57 此時left=2 right = 3 ...
    }
}

分+合實現歸併排序

圖片.png

//int arr[]={8,4,5,7,1,3,6,2};
//int temp[]=new int[arr.length];
//mergeSort(arr,0,arr.length-1,temp);
//System.out.println("並歸排序後"+ Arrays.toString(arr));
//分解方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
    if (left < right) {
        int mid = (left + right) / 2; //中間索引
        //向左遞歸進行分解
        //0 - mid 便是0 - 3 {8,4,5,7}
        mergeSort(arr, left, mid, temp);
        //向右遞歸進行分解
        //mid+1 - midright 便是4 - 7  {1,3,6,2}
        mergeSort(arr, mid + 1, right, temp);
        //進行合併
        merge(arr,left,mid,right,temp);
    }
}

運行結果以下:
tempLeft:0     rigth:1
tempLeft:2     rigth:3
tempLeft:0     rigth:3
tempLeft:4     rigth:5
tempLeft:6     rigth:7
tempLeft:4     rigth:7
tempLeft:0     rigth:7
並歸排序後[1, 2, 3, 4, 5, 6, 7, 8]

3、複雜度分析

時間複雜度: T(n)

歸併算法是一個不斷遞歸的過程假設數組的元素個數是n
時間複雜度是T(n)的函數: T(n) = 2*T(n/2) + O(n)ide

怎麼解這個公式呢?

對於規模爲n的問題,一共要進行log(n)層大小切分;
每一層的合併複雜度都是O(n);
因此總體的複雜度就是O(nlogn)函數

空間複雜度: O(n)

因爲合併n個元素須要分配一個大小爲n的額外數組,合併完成以後,這個數組的空間就會被釋放學習

相關文章
相關標籤/搜索