基礎(chǔ)排序之歸并排序

Start

前言

歸并排序(MERGE-SORT)是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個(gè)非常典型的應(yīng)用。將已有序的子序列合并,得到完全有序的序列;即先使每個(gè)子序列有序,再使子序列段間有序。若將兩個(gè)有序表合并成一個(gè)有序表,稱為二路歸并?!栋俣劝倏啤?/p>

1. 歸并排序

歸并過程為:比較 a[i] 和 b[j] 的大小,若 a[i] ≤ b[j],則將第一個(gè)有序表中的元素 a[i] 復(fù)制到 r[k] 中,并令 i 和 k 分別加上 1;否則將第二個(gè)有序表中的元素 b[j] 復(fù)制到 r[k] 中,并令 j 和 k 分別加 上1,如此循環(huán)下去,直到其中一個(gè)有序表取完,然后再將另一個(gè)有序表中剩余的元素復(fù)制到r中從下標(biāo) k 到下標(biāo) t 的單元。歸并排序的算法我們通常用遞歸實(shí)現(xiàn),先把待排序區(qū)間 [s,t] 以中點(diǎn)二分,接著把左邊子區(qū)間排序,再把右邊子區(qū)間排序,最后把左區(qū)間和右區(qū)間用一次歸并操作合并成有序的區(qū)間 [s,t]。

原理:

歸并操作的工作原理如下:
第一步:申請(qǐng)空間,使其大小為兩個(gè)已經(jīng)排序序列之和,該空間用來存放合并后的序列
第二步:設(shè)定兩個(gè)指針,最初位置分別為兩個(gè)已經(jīng)排序序列的起始位置
第三步:比較兩個(gè)指針?biāo)赶虻脑兀x擇相對(duì)小的元素放入到合并空間,并移動(dòng)指針到下一位置
重復(fù)步驟3直到某一指針超出序列尾
將另一序列剩下的所有元素直接復(fù)制到合并序列尾

總結(jié):

  • 將兩個(gè)已排好序的數(shù)組合并成一個(gè)有序的數(shù)組,稱之為歸并排序
  • 步驟:遍歷兩個(gè)數(shù)組,比較它們的值。誰比較小,誰先放入大數(shù)組中,直到數(shù)組遍歷完成

1. 演算歸并排序過程

現(xiàn)在我有兩個(gè)已經(jīng)排好順序的數(shù)組:int[] arr1 = {2, 7, 8}和int[] arr2 = {1, 4, 9},我還有一個(gè)大數(shù)組來裝載它們int[] arr = new int[6];

1.1 第一輪

那么,我將兩個(gè)數(shù)組的值進(jìn)行比較,誰的值比較小,誰就放入大數(shù)組中!

首先,拿出 arr1[0] 和 arr2[0] 進(jìn)行比較,顯然是 arr2[0] 比較小,因此將 arr2[0] 放入大數(shù)組中,同時(shí) arr2 指針往后一格。

所以,現(xiàn)在目前為止arr = {1}。

1.2 第二輪

隨后,拿 arr1[0] 和 arr2[1] 進(jìn)行比較,顯然是 arr1[0] 比較小,將 arr1[0] 放入大數(shù)組中,同時(shí) arr1 指針往后一格。

所以,現(xiàn)在目前為止arr = {1,2}。

1.3 第三輪

隨后,拿 arr1[1] 和 arr2[1] 進(jìn)行比較,顯然是 arr2[1] 比較小,將 arr2[1] 放入大數(shù)組中,同時(shí) arr2 指針往后一格。

所以,現(xiàn)在目前為止 arr = {1,2,4}。

……..

遍歷到最后,我們會(huì)將兩個(gè)已排好序的數(shù)組變成一個(gè)已排好序的數(shù)組 arr = {1,2,4,7,8,9}。

2. 歸并排序前提分析(分治法)

從上面的演算我們就直到,歸并排序的前提是需要兩個(gè)已經(jīng)排好順序的數(shù)組,那往往不會(huì)有兩個(gè)已經(jīng)排好順序的數(shù)組給我們的呀(一般是雜亂無章的一個(gè)數(shù)組),那這個(gè)算法是不是很雞肋的呢??

其實(shí)并不是的,首先假設(shè)題目給出的數(shù)組是這樣子的:int[] arr = {2, 7, 8, 1, 4, 9};

當(dāng)我們要做歸并的時(shí)候就以 arr[3] 也就元素為 1的 那個(gè)地方分開。是然后用一個(gè)指針 L 指向 arr[0],一個(gè)指針 M 指向 arr[3],用一個(gè)指針 R 指向 arr5。有了指針的幫助,我們就可以將這個(gè)數(shù)組切割成是兩個(gè)有序的數(shù)組了(操作的方式就可以和上面一樣了)。

可是上面說了,一般給出的是雜亂無章的一個(gè)數(shù)組,現(xiàn)在還是達(dá)不到要求。比如給出的是這樣一個(gè)數(shù)組:int[] arrays = {9, 2, 5, 1, 3, 2, 9, 5, 2, 1, 8};

此時(shí),我們就得用到分治的思想了:

那么我們也可以這樣想將 int[] arr = {2, 7, 8, 1, 4, 9};數(shù)組分隔成一份一份的,arr[0] 它是一個(gè)有序的"數(shù)組",arr[1] 它也是一個(gè)有序的"數(shù)組",利用指針 (L,M,R) 又可以像操作兩個(gè)數(shù)組一樣進(jìn)行排序。最終合成{2,7}…….再不斷拆分合并,最后又回到了我們的arr = {1,2,4,7,8,9},因此歸并排序是可以排序雜亂無章的數(shù)組的。
這就是我們的分治法--->將一個(gè)大問題分成很多個(gè)小問題進(jìn)行解決,最后重新組合起來。

3. 歸并代碼實(shí)現(xiàn)

實(shí)現(xiàn)步驟:

  • 拆分
  • 合并
/**
 * 歸并排序
 * 分解待排序的數(shù)組成兩個(gè)各具 n/2 個(gè)元素的子數(shù)組,遞歸調(diào)用歸并排序兩個(gè)子數(shù)組,合并兩個(gè)已排序的子數(shù)組成一個(gè)已排序的數(shù)組
 */
public class MergeSort {

    public static void main(String[] args) {
        int[] sort = SortManager.sortArr;
        int[] temp = new int[sort.length];

        mergeSort(sort, temp, 0, sort.length - 1);

        for (int i = 0; i < sort.length; i++) {
            System.out.println("輸出結(jié)果:" + sort[i]);
        }
    }

    /**
     * 歸并排序
     *
     * @param arrays
     * @param L      指向數(shù)組第一個(gè)元素
     * @param R      指向數(shù)組最后一個(gè)元素
     */

    public static void mergeSort(int[] arrays, int[] temp, int L, int R) {
        // 當(dāng)left == right時(shí),不需要再劃分
        if (L < R) {
            //取中間的數(shù),進(jìn)行拆分
            int M = (L + R) / 2;

            //左邊的數(shù)不斷拆分
            mergeSort(arrays, temp, L, M);

            //右邊的數(shù)不斷拆分
            mergeSort(arrays, temp, M + 1, R);

            //合并
            merge(arrays, temp, L, M, R);
        }

    }

    /**
     * 合并數(shù)組
     *
     * @param arrays
     * @param L      指向數(shù)組第一個(gè)元素
     * @param M      指向數(shù)組分隔的元素
     * @param R      指向數(shù)組最后的元素
     */
    private static void merge(int[] arrays, int[] temp, int L, int M, int R) {

        int i = L, j = M + 1;
        // arrays數(shù)組的第一個(gè)元素
        int k = 0;

        //比較這兩個(gè)數(shù)組的值,哪個(gè)小,就往數(shù)組上放
        while (i <= M && j <= R) {
            temp[k++] = arrays[i] < arrays[j] ? arrays[i++] : arrays[j++];
        }

        //如果左邊的數(shù)組還沒比較完,右邊的數(shù)都已經(jīng)完了,那么將左邊的數(shù)抄到大數(shù)組中(剩下的都是大數(shù)字)
        while (i <= M) {
            temp[k++] = arrays[i++];
        }

        //如果右邊的數(shù)組還沒比較完,左邊的數(shù)都已經(jīng)完了,那么將右邊的數(shù)抄到大數(shù)組中(剩下的都是大數(shù)字)
        while (j <= R) {
            temp[k++] = arrays[j++];
        }

        // 把temp數(shù)據(jù)復(fù)制回原數(shù)組
        for (i = 0; i < k; i++) {
            arrays[L + i] = temp[i];
        }
    }
}

參考文章:https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484058&idx=1&sn=432c2dd8e4bda662ce066c09f8e22bda&chksm=ebd7439bdca0ca8ded40d0f431db411928936db9b4b5f5595027c8acd2efdef5ba35348641d2#rd

申明:開始的圖片來源網(wǎng)絡(luò),侵刪

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在Java常見排序基礎(chǔ) - 上中主要介紹了冒泡排序、選擇排序、插入排序三種基礎(chǔ)排序,本篇文章主要介紹的是 快速排序...
    騎小豬看流星閱讀 1,579評(píng)論 0 13
  • 歸并排序就這么簡(jiǎn)單 從前面已經(jīng)講解了冒泡排序、選擇排序、插入排序,快速排序了,本章主要講解的是歸并排序,希望大家看...
    Java3y閱讀 598評(píng)論 2 6
  • functionswap(arr,i,j){ vartemp=arr[i]; arr[i]=arr[j]; arr...
    sweetytang閱讀 428評(píng)論 0 1
  • 今天換了個(gè)手機(jī),安裝簡(jiǎn)書APP時(shí)提示輸入性別和生日,沒多想就隨手填了,進(jìn)入發(fā)現(xiàn)可以領(lǐng)10個(gè)鉆。 錯(cuò)過了新手任務(wù)的老...
    文介閱讀 996評(píng)論 4 52
  • 天道是損有余,補(bǔ)不足 人道是補(bǔ)有余,損不足 尊重其規(guī)律,按勢(shì)而行。 道德,道理,是非對(duì)錯(cuò)皆偏向于能量~
    李娜_b39c閱讀 92評(píng)論 0 0

友情鏈接更多精彩內(nèi)容