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

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

算法圖示:git

算法解釋:把一個較大的數組不斷劃分爲較小的兩個數組,直到沒法再切分以後再作逆向合併,並再合併的過程當中調整順序。歸併算法的難點是如何儘量的減小額外存儲空間的使用。算法

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++代碼示例:ide

#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++];
        }
    }
}

自頂向下的歸併排序算法的動態圖示:測試

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

算法解釋:首先以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]直到完成整個數組的第二輪調整。code

Java代碼示例:blog

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

相關文章
相關標籤/搜索