排序--歸并排序

版權(quán)聲明:本文源自簡(jiǎn)書(shū)tianma逞泄,轉(zhuǎn)載請(qǐng)務(wù)必注明出處:http://www.reibang.com/p/df8a9e136295

概念

歸并排序就是利用歸并的思想實(shí)現(xiàn)的排序算法石窑。歸并排序的原理:假設(shè)初始序列含有n個(gè)記錄,該序列可以看成n個(gè)有序的子序列神帅,其中每個(gè)子序列的長(zhǎng)度為1讹俊,然后兩兩歸并牵啦,得到?n/2?(?x?表示不小于x的最小整數(shù))個(gè)長(zhǎng)度為2或者1的子序列迫皱,然后再兩兩歸并,……昙沦,如此重復(fù)直到得到1個(gè)長(zhǎng)度為n的有序序列為止琢唾。

遞歸式歸并

演示
比如我們待排序的數(shù)組是 {9, 1, 5, 8, 3, 7, 4, 6, 2},那么遞歸式的歸并排序?yàn)榱鞒虨椋?/p>

                 [9, 1, 5, 8, 3, 7, 4, 6, 2]
                    ↓                    ↓
          [9, 1, 5, 8, 3]             [7, 4, 6, 2]
           ↓            ↓              ↓         ↓ 
     [9, 1, 5]        [8, 3]      [7, 4]      [6, 2]
      ↓      ↓        ↓    ↓       ↓   ↓      ↓   ↓  
 [9, 1]     [5]      [8]  [3]     [7] [4]    [6]  [2]
  ↓  ↓       ↓        ↓    ↓       ↓   ↓      ↓   ↓  
[9]  [1]    [5]      [8]  [3]     [7] [4]    [6]  [2] // 上面為拆分盾饮,下面為歸并(合并)
    ↓        ↓        ↓    ↓       ↓   ↓      ↓   ↓  
 [1, 9]     [5]      [8]  [3]     [7] [4]    [6]  [2]
      ↓      ↓        ↓    ↓        ↓  ↓      ↓   ↓
     [1, 5, 9]       [3, 8]        [4, 7]     [2, 6]
           ↓           ↓              ↓         ↓
         [1, 3, 5, 9, 8]              [2, 4, 6, 7]
                   ↓                    ↓
                 [1, 2, 3, 4, 5, 6, 7, 8, 9]

Java實(shí)現(xiàn)

// 定義接口
interface Sorter {
    /**
     * 將數(shù)組按升序排序
     */
    int[] sort(int[] arr);

    /**
     * 交換數(shù)組arr中的第i個(gè)位置和第j個(gè)位置的關(guān)鍵字
     */
    default void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

// 遞歸式歸并排序
class MergeSorter implements Sorter {

    @Override
    public int[] sort(int[] arr) {
        int[] result = new int[arr.length];
        mergeSort(arr, result, 0, arr.length - 1);
        return result;
    }

    /**
     * 將亂序的src[start...end]歸并排序?yàn)橛行虻膁es[start...end]
     * 
     * @param src
     *            歸并前亂序數(shù)組
     * @param des
     *            歸并后的有序數(shù)組
     * @param start
     *            歸并的起始位置
     * @param end
     *            歸并的終止位置
     */
    private void mergeSort(int[] src, int[] des, int start, int end) {
        if (start == end) {
            des[start] = src[start];
            return;
        }
        int[] tmp = new int[src.length];
        // 將src[start...end]分為src[start...mid]和src[mid+1...end]兩部分
        int mid = (start + end) / 2;
        mergeSort(src, tmp, start, mid); // 遞歸采桃,將src[start...mid]歸并為有序的tmp[start...mid]
        mergeSort(src, tmp, mid + 1, end); // 遞歸,將src[mid+1...end]歸并為有序的tmp[mid+1...end]
        // 將有序的tmp[start...mid]和tmp[mid+1...end]合并為des[start...end]
        merge(tmp, des, start, mid, end);
    }

    /**
     * 將有序的src[start, mid]和有序的src[mid+1, end]合并為有序的des[start,end];
     * src可能為亂序數(shù)組,但是src[start, mid]和src[mid+1, end]是有序的丘损。
     * 
     * @param src
     *            亂序的原數(shù)組
     * @param des
     *            有序的目標(biāo)數(shù)組
     * @param start
     *            數(shù)組第一部分起始位置
     * @param mid
     *            數(shù)組第一部分結(jié)束位置(兩部分的分界點(diǎn))
     * @param end
     *            數(shù)組第二部分結(jié)束位置
     */
    protected void merge(int[] src, int[] des, int start, int mid, int end) {
        int i; // src數(shù)組第一部分下標(biāo)
        int j; // src數(shù)組第二部分下標(biāo)
        int k; // des數(shù)組下標(biāo)
        // 將較小的數(shù)依次移動(dòng)到目標(biāo)數(shù)組中
        for (i = start, k = start, j = mid + 1; i <= mid && j <= end;) {
            if (src[i] < src[j]) {
                des[k] = src[i++];
            } else {
                des[k] = src[j++];
            }
            k++;
        }
        // 將剩余的src[i...mid]復(fù)制到des數(shù)組中
        for (; i <= mid; i++) {
            des[k] = src[i];
            k++;
        }

        // 將剩余的src[j...end]復(fù)制到des數(shù)組中
        for (; j <= end; j++) {
            des[k] = src[j];
            k++;
        }
    }
}

復(fù)雜度
時(shí)間復(fù)雜度:
因?yàn)闅w并的遞歸操作其實(shí)就是二叉樹(shù)的結(jié)構(gòu)普办,故而,最好情況 = 最壞情況 = 平均情況 = O(nlogn)

空間復(fù)雜度:
因?yàn)檫f歸式歸并需要(1)與原始記錄相同大小的空間來(lái)存放歸并的結(jié)果以及(2)深度為logn的椗窃浚空間衔蹲,所以空間復(fù)雜度為O(n+logn)

非遞歸式歸并

演示
又比如我們待排序的數(shù)組是 {9, 1, 5, 8, 3, 7, 4, 6, 2},那么非遞歸式的歸并排序?yàn)榱鞒虨椋?/p>

 [9] [1]    [5] [8]    [3] [7]   [4] [6]  [2]
   ↓  ↓      ↓   ↓      ↓    ↓     ↓   ↓   ↓
  [1, 9]    [5, 8]      [3, 7]    [4, 6]  [2]
    ↓          ↓          ↓       ↓        ↓ 
    [1, 5, 8, 9]       [3, 4, 6, 7]       [2]
             ↓            ↓                ↓
        [1, 3, 4, 5, 6, 7, 8, 9]          [2]
                ↓                         ↓
                [1, 2, 3, 4, 5, 6, 7, 8, 9]

Java實(shí)現(xiàn)

// 非遞歸式歸并排序
class NonRecursiveMergeSorter extends MergeSorter {

    @Override
    public int[] sort(int[] arr) {
        mergeSort(arr);
        return arr;
    }

    private void mergeSort(int[] arr) {
        int len = arr.length;
        int result[] = new int[len];
        int k = 1;
        while (k < len) {
            mergePass(arr, result, k); // arr歸并至result,此時(shí)間隔為k
            k = 2 * k; // 子序列長(zhǎng)度加倍
            mergePass(result, arr, k); // result歸并至arr,此時(shí)間隔翻倍
            k = 2 * k; // 子序列長(zhǎng)度加倍
        }
    }

    /**
     * 將數(shù)組src中相鄰長(zhǎng)度為interval的子序列兩兩歸并到des數(shù)組中
     * 
     * @param src
     *            源數(shù)組
     * @param des
     *            目標(biāo)數(shù)組
     * @param interval
     *            兩兩合并的子序列長(zhǎng)度
     */
    private void mergePass(int[] src, int[] des, int interval) {
        int i = 0;
        int len = src.length;
        while (i + 2 * interval - 1 < len) {
            // 兩兩合并
            merge(src, des, i, i + interval - 1, i + 2 * interval - 1);
            i = i + 2 * interval;
        }
        if (i + interval - 1 < len - 1) {
            // i+interval-1小于len-1呈础,說(shuō)明最后還剩余兩個(gè)子序列舆驶,只不過(guò)最后的一個(gè)子序列長(zhǎng)度不夠interval
            // 那么將剩下的兩個(gè)子序列進(jìn)行合并
            merge(src, des, i, i + interval - 1, len - 1);
        } else {
            // 否則,最后只剩下單個(gè)子序列而钞,則直接將該子序列加入到des尾部
            for (; i < len; i++) {
                des[i] = src[i];
            }
        }
    }
}

復(fù)雜度
時(shí)間復(fù)雜度:
同遞歸式歸并沙廉,最好情況 = 最壞情況 = 平均情況 = O(nlogn)

空間復(fù)雜度:
非遞歸式歸并不需要保存方法棧信息,所以空間復(fù)雜度為O(n)

所以非遞歸的遞歸算法性能要高于遞歸式歸并算法笨忌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蓝仲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子官疲,更是在濱河造成了極大的恐慌,老刑警劉巖亮隙,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件途凫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡溢吻,警方通過(guò)查閱死者的電腦和手機(jī)维费,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)果元,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人犀盟,你說(shuō)我怎么就攤上這事而晒。” “怎么了阅畴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵倡怎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我贱枣,道長(zhǎng)监署,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任纽哥,我火速辦了婚禮钠乏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘春塌。我一直安慰自己晓避,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布只壳。 她就那樣靜靜地躺著俏拱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吕世。 梳的紋絲不亂的頭發(fā)上彰触,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音命辖,去河邊找鬼况毅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尔艇,可吹牛的內(nèi)容都是我干的尔许。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼终娃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼味廊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起棠耕,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤余佛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后窍荧,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體辉巡,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年蕊退,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郊楣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憔恳。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖净蚤,靈堂內(nèi)的尸體忽然破棺而出钥组,到底是詐尸還是另有隱情,我是刑警寧澤今瀑,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布程梦,位于F島的核電站,受9級(jí)特大地震影響放椰,放射性物質(zhì)發(fā)生泄漏作烟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一砾医、第九天 我趴在偏房一處隱蔽的房頂上張望拿撩。 院中可真熱鬧,春花似錦如蚜、人聲如沸压恒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)探赫。三九已至,卻和暖如春撬呢,著一層夾襖步出監(jiān)牢的瞬間伦吠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工魂拦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毛仪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓芯勘,卻偏偏與公主長(zhǎng)得像箱靴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荷愕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個(gè)記錄插入到已排序好...
    依依玖玥閱讀 1,258評(píng)論 0 2
  • 概述 排序有內(nèi)部排序和外部排序衡怀,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大安疗,一次不能容納全部...
    蟻前閱讀 5,186評(píng)論 0 52
  • 概述:排序有內(nèi)部排序和外部排序抛杨,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大荐类,一次不能容納全部...
    每天刷兩次牙閱讀 3,732評(píng)論 0 15
  • 我想說(shuō)的是一對(duì)年輕男女的事蝶桶,稀松平常,只當(dāng)故事掉冶,聽(tīng)聽(tīng)就罷真竖。暫且叫他們宿與德森。 宿是普通不過(guò)的女子厌小,算不上漂亮恢共,也...
    iceaswater閱讀 313評(píng)論 0 1
  • “江南憶讨韭,最憶是杭州。山寺月中尋桂子癣蟋,郡亭枕上看潮頭透硝。何日更重游》杞粒”昨晚濒生,G20杭州峰會(huì)文藝晚會(huì)《最憶是杭州》在西...
    正能量收獲閱讀 157評(píng)論 0 0