1. 程式人生 > >小橙書閱讀指南(五)——歸並排序的兩種實現

小橙書閱讀指南(五)——歸並排序的兩種實現

system .so generator tor ide ret style 鏈接 步長

算法描述:將兩個較小的有序數組合並成為一個較大的有序數組是比較容易的事情。我們只需要按照相同的順序依次比較最左側的元素,然後交替的放進新數組即可。這就是自頂向下的歸並排序的實現思路。與之前的算法不同的是,歸並排序需要使用額外的存儲空間,用空間換時間的做法也是在排序算法中經常需要做的選擇。

算法圖示:

技術分享圖片

算法解釋:把一個較大的數組不斷劃分為較小的兩個數組,直到無法再切分之後再做逆向合並,並再合並的過程中調整順序。歸並算法的難點是如何盡可能的減少額外存儲空間的使用。

Java代碼示例:

package algorithms.sorting;

import algorithms.Sortable;
import algorithms.common.ArraysGenerator; import java.util.Arrays; public class Merge implements Sortable<Integer> { private Integer[] aux; @Override public void sort(Integer[] array) { aux = new Integer[array.length]; sort(array, 0, array.length - 1); }
private void sort(Integer[] array, int lo, int hi) { // 遞歸結束條件 if (hi <= lo) { return; } int mid = lo + (hi - lo) / 2; sort(array, lo, mid); sort(array, mid + 1, hi); merge(array, lo, mid, hi); } private void merge(Integer[] array, int
lo, int mid, int hi) { int i = lo; int j = mid + 1; // aux數組作為成員變量,長度與array相同。重復使用,以節約存儲空間。 for (int index = lo; index <= hi; ++index) { aux[index] = array[index]; } for (int index = lo; index <= hi; ++index) { // 如果低位數組用完 則 將高位數組依次復制 if (i > mid) { array[index] = aux[j++]; } // 如果高位數組用完 則 將低位數組依次復制 else if (j > hi) { array[index] = aux[i++]; } // 如果高位數組最左側元素 小於 低位數組最左側元素 則 將高位數組最左側元素復制 else if (aux[j] < aux[i]) { array[index] = aux[j++]; } // 如果低位數組最左側元素 小於或等於 高位數組最左側元素 則 將低位數組最左側元素復制 else { array[index] = aux[i++]; } } } public static void main(String[] args) { Integer[] array = ArraysGenerator.generate(10, 0, 100); Merge merge = new Merge(); merge.sort(array); System.out.println(Arrays.toString(array)); } }

Qt/C++代碼示例:

#include "merge.h"

Merge::Merge()
{

}

Merge::~Merge()
{
    if (aux) {
        delete aux;
    }
}

void Merge::sort(int *arr, int len)
{
    if (aux) {
        delete aux;
    }
    aux = new int[len];
    sort(arr, 0, len - 1);
}

void Merge::sort(int *arr, int lo, int hi)
{
    if (hi <= lo) {
        return;
    }
    int mid = lo + (hi - lo) / 2;
    sort(arr, lo, mid);
    sort(arr, mid + 1, hi);
    merge(arr, lo, mid, hi);
}

void Merge::merge(int *arr, int lo, int mid, int hi)
{
    int loIndex = lo; // 低位數組起始坐標
    int hiIndex = mid + 1; // 高位數組其實坐標
    // 復制數組
    for (int i = lo; i <= hi; ++i) {
        aux[i] = arr[i];
    }

    for (int i = lo; i <= hi; ++i) {
        if (loIndex > mid) {
            arr[i] = aux[hiIndex++];
        }
        else if (hiIndex > hi) {
            arr[i] = aux[loIndex++];
        }
        else if (aux[hiIndex] < aux[loIndex]) {
            arr[i] = aux[hiIndex++];
        }
        else if (aux[loIndex] <= aux[hiIndex]) {
            arr[i] = aux[loIndex++];
        }
    }
}

自頂向下的歸並排序算法的動態圖示:

技術分享圖片

自底向上的歸並排序算法的動態圖示:

技術分享圖片

算法解釋:首先以1為步長調整array[i]和array[i+1],接著是array[2*i]和array[2*i+1]直到完成整個數組的第一輪調整。接下來以2為步長調整array[i],array[i+1]和array[2*i],array[2*i+1]直到完成整個數組的第二輪調整。

Java代碼示例:

package algorithms.sorting;

import algorithms.Sortable;
import algorithms.common.ArraysGenerator;

public class MergeBU implements Sortable<Integer> {
    private Integer[] aux;

    @Override
    public void sort(Integer[] array) {
        aux = new Integer[array.length];
        for (int len = 1; len < array.length; len = 2 * len) { // 每次選取的子數組的長度
            for (int lo = 0; lo < array.length - len; lo += 2 * len) {
                merge(array, lo, lo + len - 1, Math.min(lo + (2 * len) - 1, array.length - 1));
            }
        }
    }

    private void merge(Integer[] array, int lo, int mid, int hi) {
        int loIdx = lo;
        int hiIdx = mid + 1;
        for (int i = lo; i <= hi; ++i) {
            aux[i] = array[i];
        }
        for (int i = lo; i <= hi; ++i) {
            if (loIdx > mid) {
                array[i] = aux[hiIdx++];
            } else if (hiIdx > hi) {
                array[i] = aux[loIdx++];
            } else if (aux[hiIdx] < aux[loIdx]) {
                array[i] = aux[hiIdx++];
            } else {
                array[i] = aux[loIdx++];
            }
        }
    }

    public static void main(String[] args) {
        Integer[] array = ArraysGenerator.generate(1000, 0, 9999);
        MergeBU mergeBU = new MergeBU();

        mergeBU.sort(array);
        System.out.println(ArraysGenerator.isSort(array, "asc"));
    }
}

Qt/C++代碼示例(略)

有關算法效率的解釋很多,不過按照我自己的測試歸並算法是目前俠侶最高的排序算法。

相關鏈接:

Algorithms for Java

Algorithms for Qt

小橙書閱讀指南(五)——歸並排序的兩種實現