數據結構與算法之排序算法(四):歸併排序

歸併排序(分治思想的運用)

原理:將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每一個子序列是有序的,而後再把有序的子序列合併爲成天有序序列。算法

分治算法步驟:

第一步:劃分。將原問題劃分爲幾個子問題數組

第二步:遞歸求解。遞歸求解每一個子問題緩存

第三步:合併。將求解後的子問題合併成原問題spa

代碼實現(遞歸):
void mergeSort(int[] arr){
    sort(arr,0,arr.length-1);
}
void sort(int[] arr, int left, int right){
    if(left >= right){return;}
    //找出中間索引
    int middle = left + (right - left) / 2;
    //對左邊數組進行排序
    sort(arr,left,middle);
    ////對右邊數組進行排序
    sort(arr,middle + 1,right);
    //合併
    merge(arr,left,middle,right);
    print(arr);
}
/*將兩個數組進行歸併,歸併前兩個數組有序,歸併後依然有序
參數列表:arr:數組對象。left:左數組的第一個元素索引。middle:左數組的最後一個元素索引。right:右數組的最後一個元素索引*/
void merge(int[] arr, int left, int middle, int right){
     //臨時數組
    int[] tmpArr = new int[arr.length];
     //臨時數組的索引
    int tmpArrIndex = left;
    //右數組的第一個索引
    int rightFirst = middle + 1;
    //緩存數組第一個元素的索引
    int tmp = left;
    while(left <= middle && rightFirst <= right){
        //從兩個子數組中取出最小的放臨時數組中
        if(arr[left] <= arr[rightFirst]){
            tmpArr[tmpArrIndex++] = arr[left++];
        }else{
            tmpArr[tmpArrIndex++] = arr[rightFirst++];
        }
    }
    //剩餘部分依次放入臨時數組
    while(left <= middle){
        tmpArr[tmpArrIndex++] = arr[left++];
    }
    while(rightFirst <= right){
        tmpArr[tmpArrIndex++] = arr[rightFirst++];
    }
    //將臨時數組的內容拷貝回原數組中
    while(tmp <= right){
        arr[tmp] = tmpArr[tmp++];
    }
}
void print(int[] arr){
    for(int i: arr){
        System.out.print(i + "\t");
    }
}

分析:算法穩定,空間複雜度【數組O(n+logn)logn爲遞歸時使用的深度爲logn的棧空間,鏈表O(logn)】,時間複雜度【最好、平均、最壞均爲O(nlogn)】code

代碼實現(非遞歸):對象

void mergeSortNonRecursive(int[] arr){
    if(arr == null || arr.length <= 1){return;}
    //當前的歸併的子序列長度爲1
    int len = 1;
    while(len <= arr.length){
    //每次從數組第一個元素開始,每通過len*2個元素對相鄰子序列進行歸併,並注意對歸併長度段爲奇數時以及最後一個歸併段與前面不等時的處理
        for(int i=0; i+len <= arr.length - 1; i += len * 2){
           //左數組的第一個元素索引
            int left = i;
            //左數組的最後一個元素索引
            int mid = i + len -1;
            //右數組的最後一個元素索引
            int right = i + len * 2 -1;
            //邊界處理
            if(right > arr.length - 1){right = arr.length -1;}
            //merge參考遞歸排序的merge
            merge(arr,left,mid,right);
        }
        len *= 2;
    }
    return;
}

分析:在空間上避免了遞歸時使用的深度爲O(logn)的棧空間,在時間上,避免了遞歸,在時間上也有所提高。 blog

相關文章
相關標籤/搜索