歸并排序

    public static void sort(Comparable[] a) {
        Comparable[] aux = new Comparable[a.length];
        sort(a, aux, 0, a.length - 1);
        assert isSorted(a);
    }

    private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
        if (hi <= lo)
            return;
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid);
        sort(a, aux, mid + 1, hi);
        merge(a, aux, lo, mid, hi);
    }

    private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {

        assert isSorted(a, lo, mid);
        assert isSorted(a, mid + 1, hi);

        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k];
        }

        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++) {
            if (i > mid)
                a[k] = aux[j++];
            else if (j > hi)
                a[k] = aux[i++];
            else if (less(aux[j], aux[i]))
                a[k] = aux[j++];
            else
                a[k] = aux[i++];
        }
        assert isSorted(a, lo, hi);
    }

    private static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w) < 0;
    }

    private static boolean isSorted(Comparable[] a) {
        return isSorted(a, 0, a.length - 1);
    }

    private static boolean isSorted(Comparable[] a, int lo, int hi) {
        for (int i = lo + 1; i <= hi; i++)
            if (less(a[i], a[i - 1]))
                return false;
        return true;
    }

上述時間復(fù)雜度為O(nlgn),空間復(fù)雜度為O(n),適用于排序大列表因悲,基于分治法。
下列圖片來展示長度16數(shù)組歸并過程


歸并結(jié)果軌跡
調(diào)用軌跡

歸并排序有以下幾點優(yōu)化方法:

  1. 和快速排序一樣,對于小數(shù)組可以使用插入排序或者選擇排序窥岩,避免遞歸調(diào)用。
  2. 在merge()調(diào)用之前宰缤,可以判斷一下a[mid]是否小于等于a[mid+1]颂翼。如果是的話那么就不用歸并了,數(shù)組已經(jīng)是有序的慨灭。原因很簡單朦乏,既然兩個子數(shù)組已經(jīng)有序了,那么a[mid]是第一個子數(shù)組的最大值氧骤,a[mid+1]是第二個子數(shù)組的最小值呻疹。當(dāng)a[mid]<=a[mid+1]時,數(shù)組整體有序筹陵。
  3. 為了節(jié)省將元素復(fù)制到輔助數(shù)組作用的時間刽锤,可以在遞歸調(diào)用的每個層次交換原始數(shù)組與輔助數(shù)組的角色镊尺。
  4. 在merge()方法中的歸并過程需要判斷i和j是否已經(jīng)越界,即某半邊已經(jīng)用盡并思÷可以用另一種方式,去掉檢測是否某半邊已經(jīng)用盡的代碼宋彼。具體步驟是將數(shù)組a[]的后半部分以降序的方式復(fù)制到aux[]弄砍,然后從兩端歸并。對于數(shù)組{1,2,3}和{2,3,5},第一個子數(shù)組照常復(fù)制输涕,第二個則從后往前復(fù)制音婶,最終aux[]中的元素為{1,2,3,5,3,2}。這種方法的缺點是使得歸并排序變?yōu)椴环€(wěn)定排序莱坎。代碼實現(xiàn)如下:
    void merges(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
        for (int k = lo; k <= mid; k++) {
            aux[k] = a[k];
        }
        for(int k = mid+1; k <= hi; k++) {
            aux[k] = a[hi-j+mid+1];
        }
        int i = lo, j = hi;
        for (int k = lo; k <= hi; k++) {
            if (less(aux[j], aux[i])) {
                a[k] = aux[j--];
            }
            else {
                a[k] = aux[i++];
            }
        }
    }

下面代碼將上述前三種優(yōu)化結(jié)合在一起

    public void sort(Comparable[] a) {
        Comparable[] aux = a.clone();
        //將aux放在第一位
        sort(aux,a,0,a.length-1);
        assert isSorted(a);
    }

    private void sort(Comparable[] src, Comparable[] dst, int lo, int hi) {
        if (hi <= lo + CUTOFF) {
            insertionSort(dst, lo, hi);
            return;
        }
        int mid = (lo + hi)/2;
        //對調(diào)
        sort(dst, src, lo, mid);
        sort(dst, src, mid+1, hi);
        
//      if (less(src[mid], src[mid+1])) {
//          for (int i = lo; i <= hi; i++) dst[i] = src[i];
//          return;
//      }
        
        //更快
        if (less(src[mid], src[mid+1])) {
            System.arraycopy(src, lo, dst, lo, hi-lo+1);
            return;
        }
        //對調(diào)
        merge(src, dst, lo, mid, hi);
    }

    private void merge(Comparable[] src, Comparable[] dst, int lo, int mid, int hi) {
        assert isSorted(src, lo, mid);
        assert isSorted(src, mid+1, hi);
        
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if (i > mid)                    dst[k] = src[j++];
            else if (j > hi)                dst[k] = src[i++];
            else if (less(src[i], src[j]))  dst[k] = src[i++];
            else                            dst[k] = src[j++];
        }
        assert isSorted(dst, lo, hi);
    }

    private void insertionSort(Comparable[] a, int lo, int hi) {
        for (int i = lo; i <= hi; i++) {
            for (int j = i; j>lo && less(a[j], a[j-1]); j--) {
                exch(a, j, j-1);
            }
        }
    }

還有一種非遞歸版本:

    public void sort(Comparable[] a) {
        int n = a.length;
        Comparable[] aux = new Comparable[n];
        for (int len = 1; len < n; len *= 2) {
            //自底向上的歸并排序桃熄,先前一個與后一個排,再前兩個與后兩個型奥,再前四個與后四個...
            //要考慮到末尾元素不夠的情況
            for (int lo = 0; lo < n - len; lo += len * 2) {
                int hi = Math.min(lo + len * 2 - 1, n - 1);
                int mid = lo + len - 1;
                merge(a, aux, lo, mid, hi);
            }
        }
    }

    public void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
        for (int k = lo; k < hi; k++) {
            aux[k] = a[k];
        }

        int i = lo, j = mid + 1;
        for (int k = lo; k < hi; k++) {
            if (i > mid)
                a[k] = aux[j++];
            else if (j > hi)
                a[k] = aux[i++];
            else if (less(aux[j], aux[i]))
                a[k] = aux[j++];
            else
                a[k] = aux[i++];
        }
    }
1.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瞳收,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子厢汹,更是在濱河造成了極大的恐慌螟深,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烫葬,死亡現(xiàn)場離奇詭異界弧,居然都是意外死亡,警方通過查閱死者的電腦和手機搭综,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門垢箕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兑巾,你說我怎么就攤上這事条获。” “怎么了蒋歌?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵帅掘,是天一觀的道長。 經(jīng)常有香客問我堂油,道長修档,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任府框,我火速辦了婚禮吱窝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己院峡,他們只是感情好兴使,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撕予,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜈首。 梳的紋絲不亂的頭發(fā)上实抡,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音欢策,去河邊找鬼吆寨。 笑死,一個胖子當(dāng)著我的面吹牛踩寇,可吹牛的內(nèi)容都是我干的啄清。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼俺孙,長吁一口氣:“原來是場噩夢啊……” “哼辣卒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起睛榄,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤荣茫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后场靴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啡莉,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年旨剥,在試婚紗的時候發(fā)現(xiàn)自己被綠了咧欣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡轨帜,死狀恐怖魄咕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚌父,我是刑警寧澤蚕礼,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站梢什,受9級特大地震影響奠蹬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗡午,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一囤躁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦狸演、人聲如沸言蛇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腊尚。三九已至,卻和暖如春满哪,著一層夾襖步出監(jiān)牢的瞬間婿斥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工哨鸭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留民宿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓像鸡,卻偏偏與公主長得像活鹰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子只估,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

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

  • 數(shù)據(jù)結(jié)構(gòu)與算法--歸并排序 歸并排序 歸并排序基于一種稱為“歸并”的簡單操作志群。比如考試可能會分年級排名和班級排名,...
    sunhaiyu閱讀 871評論 0 6
  • Q:什么是歸并排序蛔钙?A:它是建立在歸并操作上的一種有效的排序算法赖舟;是采用分治法的一個非常典型的應(yīng)用;是一種穩(wěn)定的 ...
    TinyDolphin閱讀 2,923評論 5 4
  • 1. 基本思想 ①Divide array into two halves.②Recursively sort e...
    不會code的程序猿閱讀 368評論 0 0
  • 歸并排序 所謂歸并夸楣,就是將兩個或兩個以上的有序表合并成一個新的有序表宾抓。如下圖所示,有兩個已經(jīng)排好序的有序表A[1]...
    JackChen1024閱讀 2,356評論 0 4
  • 歸并排序的基本思想是:利用遞歸和分而治之(Divide and Conquer)的方法將待排序的數(shù)組劃分成越來越小...
    素娜93閱讀 696評論 0 14