歸并排序

一.兩個(gè)有序數(shù)組的排順

如果有兩個(gè)有序的數(shù)組合并為一個(gè)有序數(shù)組巾陕,我們可以用下面的代碼實(shí)現(xiàn):

public void merge(int[] a, int[] b, int[] c){
    int i=0,j=0,k=0;
    while (i<a.length && j<b.length){
        if (a[i]<=b[j]){
            c[k++]=a[i++];
        } else{
            c[k++]=b[j++];
        }
    }
    while (i<a.length){
        c[k++]=a[i++];
    }
    while (j<b.length){
        c[k++]=b[j++];
}

其中數(shù)組a,b為我們已經(jīng)排好序的兩個(gè)升序數(shù)組,c為我們新建的一個(gè)長(zhǎng)度為a,b兩數(shù)組相加的一個(gè)空數(shù)組。
??我們用一組測(cè)試用例來(lái)測(cè)試上述算法,并加上log使之更加清晰:

public class TwoListSort {
    public static void main(String args[]){
        int[] a = {2,6,11 };        //聲明數(shù)組
        int[] b = {8,9,9,15};
        int[] c = new int[7];
        merge(a,b,c);
    }

    public static void merge(int[] a, int[] b, int[] c){
        int i=0,j=0,k=0;
        while (i<a.length && j<b.length){
            if (a[i]<=b[j]){
                c[k++]=a[i++];
                test(c,i,j,k);
            } else{
                c[k++]=b[j++];
                test(c,i,j,k);
            }
        }
        while (i<a.length){
            c[k++]=a[i++];
            test(c,i,j,k);
        }
        while (j<b.length){
            c[k++]=b[j++];
            test(c,i,j,k);
        }
    }

    public static void test(int[] c, int i, int j, int k){
        for(int h = 0; h < c.length ; h++) {
            System.out.print( c[h] + "," );
          }
         System.out.print( "        "+ " i = "+i +", "+" j = "+j +", "+" k = "+k );
        System.out.println("");
    }
}

log輸出為:

2,0,0,0,0,0,0,         i = 1,  j = 0,  k = 1
2,6,0,0,0,0,0,         i = 2,  j = 0,  k = 2
2,6,8,0,0,0,0,         i = 2,  j = 1,  k = 3
2,6,8,9,0,0,0,         i = 2,  j = 2,  k = 4
2,6,8,9,9,0,0,         i = 2,  j = 3,  k = 5
2,6,8,9,9,11,0,         i = 3,  j = 3,  k = 6
2,6,8,9,9,11,15,         i = 3,  j = 4,  k = 7

我們對(duì)照l(shuí)og來(lái)分析一下上述過(guò)程:

初始:i=0,j=0,k=0;
a[0]<b[0]
    c[0] = a[0] = 2;
c = 2,0,0,0,0,0,0,         i = 1,  j = 0,  k = 1
a[1]<b[0]
    c[1] = a[1] = 6;
c = 2,6,0,0,0,0,0,         i = 2,  j = 0,  k = 2
a[2]>b[0]
    c[2] = b[0] = 8;
c = 2,6,8,0,0,0,0,         i = 2,  j = 1,  k = 3
a[2]>b[1]
    c[3] = b[1] = 9;
c = 2,6,8,9,0,0,0,         i = 2,  j = 2,  k = 4
a[2]>b[2]
    c[4] = b[2] = 9;
c = 2,6,8,9,9,0,0,         i = 2,  j = 3,  k = 5
a[2]<b[3]
    c[5] = a[2] = 11;
c = 2,6,8,9,9,11,0,         i = 3,  j = 3,  k = 6

此時(shí)拟赊,不滿足while (i<a.length && j<b.length){這個(gè)條件,跳出最上面的循環(huán)粹淋,進(jìn)入下面的循環(huán)中:
        while (j<b.length){
            c[k++]=b[j++];
            test(c,i,j,k);
        }
c[6] = b[3] = 15
c = 2,6,8,9,9,11,15,         i = 3,  j = 4,  k = 7

上面算法設(shè)置的非常巧妙吸祟,大家可以多測(cè)試幾組用例,針對(duì)不同情況做出分析桃移。

二.無(wú)序數(shù)組的排序

現(xiàn)在假設(shè)有一個(gè)無(wú)序數(shù)組需要排序屋匕,但是可以人為的通過(guò)一定操作劃分為兩個(gè)有序的數(shù)組A和B,那么我們借助上述算法可以很快的將A和B兩個(gè)有序的子數(shù)組進(jìn)行歸并排序借杰。
??現(xiàn)在有一個(gè)無(wú)序數(shù)組需要排序过吻,并且他是完全無(wú)序的,不能劃分為任何有序數(shù)組蔗衡,這個(gè)時(shí)候需要怎么排序呢纤虽?這個(gè)問(wèn)題可以轉(zhuǎn)換為,上一種情況中绞惦,A和B兩個(gè)子數(shù)組也是無(wú)序的逼纸,那么我們可以繼續(xù)將A和B兩個(gè)數(shù)組拆分下去,拆成更小的子數(shù)組:a1,a2和b1,b2...如此下去翩隧,我們一直將子數(shù)組拆分到最小元素樊展,即一個(gè)子數(shù)組只有一個(gè)元素(也就是拆分成一個(gè)個(gè)元素呻纹,每個(gè)元素視為長(zhǎng)度為1的數(shù)組)堆生,那么這一個(gè)個(gè)長(zhǎng)度為1的數(shù)組,就可以視為有序數(shù)組了(畢竟他只有一個(gè)元素)雷酪。
??那么此時(shí)我們可以兩個(gè)元素淑仆,視為“一.兩個(gè)有序數(shù)組的排順”情況中的A.B兩個(gè)數(shù)組,然后兩兩合并哥力,知道合并為最初長(zhǎng)度的數(shù)組蔗怠,可以看到,這實(shí)際上是一個(gè)遞歸的過(guò)程吩跋。

歸并排序.gif

看到這里不理解的話沒(méi)關(guān)系寞射,我們會(huì)在代碼中帶大家一步步分析:

public class MergeSort {
    public static void main(String args[]){
        int[] nums = {27, 8, 57, 9, 23, 41, 65, 19, 0, 1, 2 };      //聲明數(shù)組
        mergeSort(nums);
    }
    //歸并排序
    public static void mergeSort(int[] arr){
        int[] temp =new int[arr.length] ;
        internalMergeSort(arr, temp, 0, arr.length-1);
    }
    private static void internalMergeSort(int[] a, int[] b, int left, int right){
        if (left<right){    //當(dāng)left==right的時(shí),已經(jīng)不需要再劃分了
            int middle = (left+right)/2;
            internalMergeSort(a, b, left, middle);          //左子數(shù)組
            internalMergeSort(a, b, middle+1, right);       //右子數(shù)組
            mergeSortedArray(a, b, left, middle, right);    //合并兩個(gè)子數(shù)組
        }
    }
    // 合并兩個(gè)有序子序列 arr[left, ..., middle] 和 arr[middle+1, ..., right]锌钮。temp是輔助數(shù)組桥温。
    private static void mergeSortedArray(int arr[], int temp[], int left, int middle, int right){
        int i=left;
        int j=middle+1;
        int k=0;

        while ( i<=middle && j<=right){     // 逐個(gè)歸并
            if (arr[i] <=arr[j]){
                temp[k++] = arr[i++];
            } else{
                temp[k++] = arr[j++];
            }
        }
        while (i <=middle){      // 將左邊剩余的歸并
            temp[k++] = arr[i++];
        }
        while ( j<=right){           // 將右邊剩余的歸并
            temp[k++] = arr[j++];
        }
        for (i=0; i<k; ++i){         //把數(shù)據(jù)復(fù)制回原數(shù)組
            arr[left+i] = temp[i];
        }
    }
}

上面展示的就是一個(gè)歸并排序的過(guò)程,internalMergeSort(int[] a, int[] b, int left, int right)該方法就是拆分?jǐn)?shù)組為一個(gè)個(gè)最小子元素的方法梁丘,可以看到該方法中動(dòng)用了遞歸調(diào)用侵浸;mergeSortedArray(int arr[], int temp[], int left, int middle, int right)則是“一.兩個(gè)有序數(shù)組的排順”情況中將兩個(gè)有序數(shù)組排序的算法旺韭,也就是歸并方法:

我們給上述兩個(gè)方法加上必要的log:

   private static void internalMergeSort(int[] a, int[] b, int left, int right){
        //當(dāng)left==right的時(shí),已經(jīng)不需要再劃分了
        if (left<right){
            int middle = (left+right)/2;
            
            System.out.println("");
            System.out.println(  "(left左 , middle左掏觉,right左) = " + " (" + left + "," + middle + "," + right + ") "  );
            for(int i = left; i < right +1;  i++) {
                System.out.print(a[i] + ",");
             }
            internalMergeSort(a, b, left, middle);          //左子數(shù)組
            
            System.out.println("");
            
            System.out.println(  "(left右 , middle右区端,right右) = " + " (" + left + "," + (middle+1) + "," + right + ") "  );
            for(int i = middle+1; i < right +1;  i++) {
                System.out.print(a[i] + ",");
             }
            internalMergeSort(a, b, middle+1, right);       //右子數(shù)組
           
            mergeSortedArray(a, b, left, middle, right);    //合并兩個(gè)子數(shù)組
        }
    }
 private static void mergeSortedArray(int arr[], int temp[], int left, int middle, int right){      
         System.out.println("");
         System.out.println(  "(left合并 , middle合并,right合并) = " + " (" + left + "," + middle + "," + right + ") "  );
         System.out.print("排序前:");
         for(int h = left; h < right +1; h++) {
            System.out.print( arr[h] + ",");
          }
         
        int i=left;     
        int j=middle+1;
        int k=0;
        
        while ( i<=middle && j<=right){     // 逐個(gè)歸并
            if (arr[i] <=arr[j]){
                temp[k++] = arr[i++];
            } else{
                temp[k++] = arr[j++];
            }
        }
        
        while (i <=middle){      // 將左邊剩余的歸并
            temp[k++] = arr[i++];
        }
        
        while ( j<=right){           // 將右邊剩余的歸并
            temp[k++] = arr[j++];
        }
       
        for (i=0; i<k; ++i){         //把數(shù)據(jù)復(fù)制回原數(shù)組
            arr[left+i] = temp[i];
        }
        
        System.out.println("");
        System.out.print("排序后:");
        for(int h = left; h < right +1; h++) {
            System.out.print( arr[h] + ",");
          }
    }

由于運(yùn)行的結(jié)果比較長(zhǎng)澳腹,我們將在下面的分析中逐步分開(kāi)講解:
下面我們來(lái)分析一遍上面的結(jié)果——首先是第一部分:

(left左 , middle左织盼,right左) =  (0,5,10) 
27,8,57,9,23,41,65,19,0,1,2,
(left左 , middle左,right左) =  (0,2,5) 
27,8,57,9,23,41,
(left左 , middle左酱塔,right左) =  (0,1,2) 
27,8,57,
(left左 , middle左悔政,right左) =  (0,0,1) 
27,8,
(left右 , middle右,right右) =  (0,1,1) 
8,
(left合并 , middle合并延旧,right合并) =  (0,0,1) 
排序前:27,8,
排序后:8,27,

這個(gè)部分的log代表的過(guò)程圖示如下谋国,即第一次循環(huán)遍歷到左子樹(shù)的最底層,開(kāi)始向上歸并一層迁沫,歸并的過(guò)程伴隨著排序:

歸并排序測(cè)試用例圖解(第一部分).png

歸并后的結(jié)果為8,27,然后向上遞歸調(diào)用芦瘾,再歸并一層:

(left右 , middle右,right右) =  (0,2,2) 
57,
(left合并 , middle合并集畅,right合并) =  (0,1,2) 
排序前:8,27,57,
排序后:8,27,57,
歸并排序測(cè)試用例圖解(第二步).png

接著歸并第三層:

(left右 , middle右近弟,right右) =  (0,3,5) 
9,23,41,
(left左 , middle左,right左) =  (3,4,5) 
9,23,41,
(left左 , middle左挺智,right左) =  (3,3,4) 
9,23,
(left右 , middle右祷愉,right右) =  (3,4,4) 
23,
(left合并 , middle合并,right合并) =  (3,3,4) 
排序前:9,23,
排序后:9,23,
(left右 , middle右赦颇,right右) =  (3,5,5) 
41,
(left合并 , middle合并二鳄,right合并) =  (3,4,5) 
排序前:9,23,41,
排序后:9,23,41,
![Uploading 歸并排序_977410.gif . . .]
(left合并 , middle合并,right合并) =  (0,2,5) 
排序前:8,27,57,9,23,41,
排序后:8,9,23,27,41,57,

這一步就是合并8,27,579,23,41這兩個(gè)有序子數(shù)組媒怯,畢竟這兩個(gè)子數(shù)組的底層已經(jīng)遞歸調(diào)用完了订讼,然后合并的過(guò)程中伴隨著排序,最終得到第二層有序的左子數(shù)組:8,9,23,27,41,57,

接著是同樣的遞歸合并第二層右子數(shù)組:

(left右 , middle右扇苞,right右) =  (0,6,10) 
65,19,0,1,2,
(left左 , middle左欺殿,right左) =  (6,8,10) 
65,19,0,1,2,
(left左 , middle左,right左) =  (6,7,8) 
65,19,0,
(left左 , middle左鳖敷,right左) =  (6,6,7) 
65,19,
(left右 , middle右脖苏,right右) =  (6,7,7) 
19,
(left合并 , middle合并,right合并) =  (6,6,7) 
排序前:65,19,
排序后:19,65,
(left右 , middle右定踱,right右) =  (6,8,8) 
0,
(left合并 , middle合并棍潘,right合并) =  (6,7,8) 
排序前:19,65,0,
排序后:0,19,65,
(left右 , middle右,right右) =  (6,9,10) 
1,2,
(left左 , middle左,right左) =  (9,9,10) 
1,2,
(left右 , middle右蜒谤,right右) =  (9,10,10) 
2,
(left合并 , middle合并山宾,right合并) =  (9,9,10) 
排序前:1,2,
排序后:1,2,
(left合并 , middle合并,right合并) =  (6,8,10) 
排序前:0,19,65,1,2,
排序后:0,1,2,19,65,

最后是歸并第二層已經(jīng)拍好隊(duì)的兩個(gè)有序子數(shù)組:8,9,23,27,41,57,0,1,2,19,65,鳍徽,最終得到有序的原數(shù)組:

(left合并 , middle合并资锰,right合并) =  (0,5,10) 
排序前:8,9,23,27,41,57,0,1,2,19,65,
排序后:0,1,2,8,9,19,23,27,41,57,65,
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市阶祭,隨后出現(xiàn)的幾起案子绷杜,更是在濱河造成了極大的恐慌,老刑警劉巖濒募,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鞭盟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瑰剃,警方通過(guò)查閱死者的電腦和手機(jī)齿诉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晌姚,“玉大人粤剧,你說(shuō)我怎么就攤上這事』舆耄” “怎么了抵恋?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)宝磨。 經(jīng)常有香客問(wèn)我弧关,道長(zhǎng),這世上最難降的妖魔是什么唤锉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任世囊,我火速辦了婚禮,結(jié)果婚禮上腌紧,老公的妹妹穿的比我還像新娘茸习。我一直安慰自己壁肋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布浸遗。 她就那樣靜靜地躺著,像睡著了一般跛锌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天菠赚,我揣著相機(jī)與錄音,去河邊找鬼衡查。 笑死,一個(gè)胖子當(dāng)著我的面吹牛必盖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播歌粥,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼塌忽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了失驶?” 一聲冷哼從身側(cè)響起土居,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嬉探,沒(méi)想到半個(gè)月后装盯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甲馋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年埂奈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片定躏。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡账磺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痊远,到底是詐尸還是另有隱情垮抗,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布碧聪,位于F島的核電站冒版,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏逞姿。R本人自食惡果不足惜辞嗡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滞造。 院中可真熱鬧续室,春花似錦、人聲如沸谒养。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至丰泊,卻和暖如春薯定,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞳购。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工沉唠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苛败。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓满葛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親罢屈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嘀韧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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

  • 數(shù)據(jù)結(jié)構(gòu)與算法--歸并排序 歸并排序 歸并排序基于一種稱為“歸并”的簡(jiǎn)單操作。比如考試可能會(huì)分年級(jí)排名和班級(jí)排名缠捌,...
    sunhaiyu閱讀 873評(píng)論 0 6
  • 歸并排序 百度上的解釋:歸并排序:是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide ...
    半月迎風(fēng)閱讀 636評(píng)論 0 4
  • 序言 上一篇文章我們已經(jīng)講完了插入排序,也就是說(shuō)我的On^2 的算法基本就寫完了曼月,當(dāng)然還有別的On^2 的算法,但...
    再見(jiàn)遠(yuǎn)洋閱讀 1,652評(píng)論 0 3
  • 歸并排序 所謂歸并炎辨,就是將兩個(gè)或兩個(gè)以上的有序表合并成一個(gè)新的有序表聪姿。如下圖所示,有兩個(gè)已經(jīng)排好序的有序表A[1]...
    JackChen1024閱讀 2,358評(píng)論 0 4
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個(gè)記錄插入到已排序好...
    依依玖玥閱讀 1,243評(píng)論 0 2