1. 程式人生 > >排序演算法——分治思想與歸併排序

排序演算法——分治思想與歸併排序

1、分治法思想

    分治思想主要通過遞迴來實現,每層遞迴中主要包括三個步驟:

  1. 分解:即將原問題劃分為若干子問題,子問題的形式要保證和原問題一致,但規模更小。
  2. 解決:當劃分子問題的規模足夠小時,停止遞迴,求解子問題,獲取子問題的解決結果並返回給上一層。
  3. 合併:將子問題的解決結果進行合併,得到原問題的解。

    也就是說,分治思想就是將一個大問題用過遞迴層層分解為無數個子問題,這些子問題要保證與與原問題是一個型別,只不過規模更小更加容易求解,但求解過程相同。

2、歸併排序演算法思想

    1、思考:如果給了我們兩個已經做好排序的陣列(相同排序規則)A和B,我們該如何將其合併到一個有序陣列C?

    這個問題很簡單,依次從兩個陣列中取元素比較然後放進C中即可。而如果A、B陣列都只有一個元素時,他們都成為了一個有序陣列,則可以直接進行比較放在陣列C中,返回有序的陣列C。

    2、通過分治思想對無序陣列排序問題進行劃分:對於一個無序的陣列array

(1)分解:通過遞迴將無序陣列進行劃分,每次從array.length/2處劃分為兩個陣列,直到劃分後的子陣列規模為1。

(2)解決:當子陣列規模為1時,此時的最小規模陣列已經自動變為一個有序陣列,遞迴終止返回有序陣列結果。

(3)合併:接收下一層遞迴所返回的兩個有序子陣列,將這兩個有序子陣列的資料進行合併為一個新的有序陣列,然後返回該有序陣列給上一層遞迴。

    3、排序例項:為方便理解遞迴劃分陣列,可以建立一個遞迴樹形圖(理解遞迴比較方便)。以陣列int[] arr = {3,23,4,13,4,5,63,6,2}為例,其陣列劃分樹形圖為

 

    那麼接下來就是將葉子節點已經排序好的陣列返回給上一層遞迴,在上一層中處理合並一個新的有序陣列再返回給上一層,重複該步驟直到根節點層就可以將原陣列變為一個有序陣列。

    也就是說,歸併演算法中遞迴呼叫的方法主要為兩部分,分別是分割陣列以及數組合(將兩個有序陣列進行合併)。虛擬碼如下

merge(int[] arr){
    if(arr.length < 2){
        return arr;//如果傳入的陣列長度為1那麼就直接返回
    }
    int[] left = splitLeft(arr);//劃分獲取左子陣列
    int[] right = spliteRight(arr);//劃分獲取右子陣列
    
    left = merge(left);//遞迴呼叫,獲得排序後的左子陣列
    right = merge(right);//遞迴呼叫,獲得排序後的右子陣列
    
    arr = sort(left,right);//將左右有序子數組合併為一個有序陣列
    return arr;//返回
}

    具體程式碼實現(個人寫的依據演算法邏輯思想寫的一個示例,劃分左右子陣列都是新建立陣列進行賦值,空間複雜度很高,而且提高了時間複雜度,但是主要是便於理解)

個人實現:

/**
 * 
 * @Description:分治演算法,利用遞迴,將一個數組元素不斷的分割為兩個陣列,直到陣列分割到只剩下一個元素
 * 然後返回給上一層,主要是保證返回給上一層中的兩個陣列是已經排好序的陣列,在上一層中進行排序合併,然後再返回
 * 就可以最終得到一個有序陣列
 */
public class Sort {
    public static int[] merge(int[] arr){
        
        int left = 0;
        int right = arr.length;
        if (right == 1) {
            int[] endArr = {arr[left]};
            return endArr;
        }
        
        //陣列劃分
        int mid = (left + right) / 2;
        int[] leftArr = new int[mid-left];
        int[] rightArr = new int[right - mid];
        for (int i = 0; i <= mid-1; i++) {
            leftArr[i] = arr[i];
        }
        for (int i = 0,j=mid; i < rightArr.length && j < arr.length; i++,j++) {
            rightArr[i] = arr[j];
        }
        
        //遞迴呼叫,獲取排序完成的左右子陣列
        leftArr = merge(leftArr);
        rightArr = merge(rightArr);
        
        //合併兩個陣列並排序
        return sort(arr, leftArr, rightArr);
    }
    public static int[] sort(int[] arr, int[] left, int[] right){
        int i = 0;
        int j = 0;
        for (int index = 0; index < arr.length; index++){
            if (i >= left.length && j < right.length) {
                arr[index] = right[j];
                j++;
                continue;
            }
            if (j >= right.length && i < left.length) {
                arr[index] = left[i];
                i++;
                continue;
            }
            if (left[i] > right[j]){
                arr[index] = right[j];
                j++; 
            } else {
                arr[index] = left[i];
                i++;
            }
        }
        return arr;
    }
    public static void main(String[] args) {
        int[] arr = {3,23,4,13,4,5,63,6,2};
        for(int i:merge(arr)) {
            System.out.println(i);
        }
    }
}

較好的實現:

public class Solution {
    /*
     * @param A:an integer array
     * @return:
     */
    public voidsortIntegers2(int[] A) {
        // writeyour code here
        //利用歸併排序對陣列A進行排序
       mergeSort(A,0,A.length-1);
    }
    //歸併排序
    public void mergeSort(int[] A,int start,int end){
       if(start>=end) return;
        int middle= (start+end)/2;
       mergeSort(A,start,middle);
       mergeSort(A,middle+1,end);
        //歸併排序需要分配的臨時陣列
        //這是歸併排序的核心
        int []temp  = new int[end-start+1];
        inti=start;
        int j =middle+1;
        intindex=0;
       while(i<=middle&&j<=end){
           if(A[i]<=A[j]){
               temp[index++] = A[i++];
            }else{
               temp[index++] = A[j++];
            }
        }
       while(i<=middle){
           temp[index++] =A[i++];
        }
       while(j<=end){
           temp[index++] =A[j++];
        }
        i=start;
        index = 0;
       for(;i<=end;i++){
            A[i] =temp[index++];
        }
    }
}

相關推薦

排序演算法——分治思想歸併排序

1、分治法思想     分治思想主要通過遞迴來實現,每層遞迴中主要包括三個步驟: 分解:即將原問題

快速排序演算法-分治思想

以下內容全部轉載自:http://www.cnblogs.com/luchen927/archive/2012/02/29/2368070.html 今天介紹快速排序,這也是在實際中最常用的一種排序演算法,速度快,效率高。就像名字一樣,快速排序是最優秀的一種排序演算法。

排序演算法(四)——歸併排序遞迴

基本思想 分析歸併排序之前,我們先來了解一下分治演算法。 分治演算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。 分治演算法的一般步驟: (1)分解,將要解決的問題劃分成若干規模較小

java排序演算法(四)------歸併排序

歸併排序: 是利用歸併的思想實現的排序方法,該演算法採用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然後遞迴求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。 合

排序演算法c語言描述---歸併排序

                排序算法系列學習,主要描述氣泡排序,選擇排序,直接插入排序,希爾排序,堆排序,歸併排序,快速排序等排序進行分析。文章規劃:一。通過自己對排序演算法本身的理解,對每個方法寫個小測試程式。 具體思路分析不展開描述。二。通過《大話資料結構》一書的截圖,詳細分析該演算法 。 在此,推薦

排序演算法:二路歸併排序(java)

public class MergeSort { /** * * @param array 待排序陣列 * @param temp 輔助陣列 * @param start 開始下標 * @param end 結束下標

五十道程式設計小題目 --- 28 八大排序演算法 java 之 07歸併排序

7. 歸併排序(Merge Sort) 基本思想: 歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然後再把有序子序列合併為整體有序序列。 歸併排序示例:   合併方法: 設r[

常見比較排序演算法的實現(歸併排序、快速排序、堆排序、選擇排序、插入排序、希爾排序

這篇部落格主要實現一些常見的排序演算法。例如: //氣泡排序 //選擇排序 //簡單插入排序 //折半插入排序 //希爾排序 //歸併排序 //雙向的快速排序(以及快速排序的非遞迴版本) //單向的快速排序 //堆排序 對於各個演算法的實現

排序演算法(七)——歸併排序

歸併排序(Merge Sort)演算法就是將多個有序資料表合併成一個有序資料表。如果參與合併的只有兩個有序表,則稱為二路合併。對於一個原始的待排序序列,往往可以通過分割的方法來歸結為多路合併排序。 合併排序演算法的運作如下: (1)首先將含有n個結點的待排序資料序列看成9個長度為1的有序子表

歸併排序演算法原理分析程式碼實現

  歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用,歸併排序將兩個已排序的表合併成一個表。 歸併排序基本原理

跟著《演算法導論》學習——插入排序歸併排序

讀前宣告:本人所寫帖子主要為了記錄本人學習的一個過程,無他想法,由於內容比較膚淺,如有雷同,非常正常!!! 本文內容: 本文主要是參考《演算法導論》這本書,完成部分演算法編寫,可能程式設計習慣或者風格比較差,還請多多批評。 1、插入排序(Insertion Sort) 插入

C語言排序(五)——插入排序歸併排序演算法比較

一.實驗內容: 1、         編寫函式分別實現插入排序和歸併排序演算法 2、         編寫主函式通過呼叫函式實現對待排資料的呼叫 3、         待排資料利用隨機函式迴圈產生10萬個以上的資料 4、         利用求系統時間的函式,分別求出2個排

分治演算法的完美使用----歸併排序

歸併排序(Merge Sort)演算法完全依照了分治模式   分解:將 n 個元素分成各含 n/2 個元素的子序列;   解決:對兩個子序列遞迴地排序;   合併:合併兩個已排序地子序列以得到排序結果;和快速排序不同的是   歸併的劃分比較隨意,快排重點就是劃分   歸併的重點就是合併,快排不需要合併 程式

O(n*logn)級別的演算法之二(快速排序)的三種實現方法詳解及其歸併排序的對比

快速排序被稱為二十世紀演算法界的一大傑作 一,單路快排 1.測試用例: #ifndef INC_06_QUICK_SORT_DEAL_WITH_NEARLY_ORDERED_ARRAY_SORTTESTHELPER_H #define INC_06_QUICK_

分治演算法(一)--歸併排序

歸併排序 最近上演算法課,老師講到分治演算法那塊了,感覺上課是認真聽了的,可是那個段子手老師講幾個笑話我就忘完了,很尷尬,所以以後她每講一章,自己還是總結下哈 分治演算法的理解 就我個人來說,對分治演算法理解很簡單,就三個字 分 解 合 分:就是把大

演算法分析——分治思想之快速排序

優化一個演算法的最根本的原理就是減少演算法的基本操作。 分治法的設計思想是,將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。 於是,在快速排序中,我們通過分割陣列的思路來將大問題分割成小規模的問題,與二分搜尋法類似的是,在二分法 中,我們需

常見排序演算法總結分析之選擇排序歸併排序-C#實現

本篇文章對選擇排序中的簡單選擇排序與堆排序,以及常用的歸併排序做一個總結分析。 [常見排序演算法總結分析之交換排序與插入排序-C#實現](https://www.cnblogs.com/iwiniwin/p/12589405.html)是排序演算法總結系列的首篇文章,包含了一些概念的介紹以及交換排序(冒泡與

演算法-優先佇列排序

優先佇列 許多應用程式都需要處理有序的元素,但不一定要求他們全部有序,或是不一定要一次就將他們排序。很多情況下我們會收集一些元素,處理當前鍵值最大的元素,然後再收集更多元素,再處理當前鍵值最大的元素,如此這般。 在這種情況下,一個合適的資料結構應該支援兩種操作:刪除最大元素和插入元素。

從零開始學演算法(四)歸併排序

從零開始學演算法(四)歸併排序 歸併排序 演算法介紹 演算法原理 演算法簡單記憶說明 演算法複雜度和穩定性 程式碼實現 歸併排序 程式碼是Javascript語言寫的(幾乎是虛擬碼) 演算

演算法第五記-歸併排序

      今天我要講的排序演算法是歸併排序,首先我想提出一個問題(很多演算法題的思路都源於此),給定兩個已排序的序列,如何將其兩個序列合併為一個大序列並且依然保持有序。思路很簡單每個小序列維持一個指標指向左邊界,然後兩個序列的左邊界進行比較大小,小的那方加入新的序列中