演算法導論學習之六:歸併排序

趙明威發表於2016-04-09

一、原理

歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為二路歸併。

歸併過程為:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元素a[i]複製到r[k]中,並令i和k分別加上1;否則將第二個有序表中的元素a[j]複製到r[k]中,並令j和k分別加上1,如此迴圈下去,直到其中一個有序表取完,然後再將另一個有序表中剩餘的元素複製到r中從下標k到下標t的單元。

歸併排序的演算法我們通常用遞迴實現,先把待排序區間[s,t]以中點二分,接著把左邊子區間排序,再把右邊子區間排序,最後把左區間和右區間用一次歸併操作合併成有序的區間[s,t]。

package sort;

import java.util.Arrays;

/**
 * Created by zhaomingwei on 16/4/9.
 */
public class MergeSortTest {
 public static void main(String[] args) {
    int[] a = {36, 24, 76, 11, 45, 64, 21, 69, 19, 36};
    mergeSort(a,0,a.length -1);
    System.out.println(Arrays.toString(a));
}
//分 + 合
public static void mergeSort(int[] a,int low,int high){
    //退出遞迴條件
    if (low >= high){
        return;
    }

    int div = (low+high)/2;
    mergeSort(a,low,div);
    mergeSort(a,div+1,high);
    merge(a,low,div,high);

}
//合併
public static void merge(int[] a,int low,int div,int high){
    int len1 = div - low + 1;
    int len2 = high - div;
    int[] L = new int[len1];
    int[] H = new int[len2];
    copyArray(a,L,low,div);
    copyArray(a,H,div+1,high);
    int i = 0;
    int j = 0;
    for (int k = low; k <= high; k++){
      if (i < len1&&j < len2 && L[i]>H[j]){
          a[k] = H[j];
          j++;
      }else if(i < len1){
          a[k] = L[i];
          i++;
      }else if(j < len2){
          a[k] = H[j];
          j++;
      }
    }

}
//複製陣列
public static void copyArray(int[] fromArr,int[] toArr,int start,int end){
    for (int i=0;i<= end - start;i++){
        toArr[i] = fromArr[start+i];
    }
}
}

根據演算法書籍上的程式碼 基本程式碼

public class BaseMethod {

/**
 * 判斷v是否小於w
 *
 * @param v
 * @param w
 * @return
 */
public static boolean less(Comparable v, Comparable w) {
    return v.compareTo(w) < 0;
}

/**
 * 交換索引為i,j位置元素
 *
 * @param a
 * @param i
 * @param j
 */
public static void exch(Comparable[] a, int i, int j) {
    Comparable t = a[i];
    a[i] = a[j];
    a[j] = t;
}

/**
 * 測試是否被排序,按升序排序
 *
 * @param a
 * @return
 */
public static boolean isSorted(Comparable[] a) {
    for (int i = 1; i < a.length; i++) {
        if (less(a[i], a[i - 1])) {
            return false;
        }
    }
    return true;
}

}

排序程式碼:

import java.util.Arrays;
public class Merge extends BaseMethod {
private static Comparable[] aux;

/**
 * 自頂向下的遞迴排序
 * @param a
 */
public static void sort(Comparable[] a) {
    aux = new Comparable[a.length];
    sort(a, 0, a.length - 1);
}


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

}

/**
 * 自底向上排序
 * @param a
 */
public static void sortDownUp(Comparable[] a){
    int n = a.length;
    aux = new Comparable[n];
    for (int sz = 1;sz<n;sz=sz+sz){
        for (int lo = 0; lo < n-sz; lo+=sz+sz) {
            merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1,n-1));
        }
    }

}

private static void merge(Comparable[] a, int lo, int mid, int hi) {
    int i = lo;
    int j = mid + 1;
    for (int k = lo; k <= hi; k++) {
        aux[k] = a[k];
    }

    for (int k = lo; k <= hi; k++) {
        if (i > mid) {
            /**左邊用盡 i>mid*/
            a[k] = aux[j++];
        } else if (j > hi) {
            /**右邊用盡 j>hi*/
            a[k] = aux[i++];
        }else if (less(aux[j],a[i])){
            a[k] = aux[j++];
        }else{
            a[k] = aux[i++];
        }
    }
}

public static void main(String[] args) {
    Integer[] a = {2, 3, 1, 8, 0, 5, 4, 6, 9, 7};
    sortDownUp(a);
    System.out.println(Arrays.toString(a));
}

}

相關文章