Java歸並排序的遞歸與非遞歸實現


該命題已有無數解釋,備份修改后的代碼

平均時間復雜度: O(NLogN)  以2為底

最好情況時間復雜度: O(NLogN)

最差情況時間復雜度: O(NLogN)

所需要額外空間: 遞歸:O(N + LogN), 非遞歸:O(N)

穩定性: 穩定

歸並排序基於分治(快排也是),利用歸並來實現排序,其基本思想是:

  如果一個數組有n個數據,則可以把這個數組看作n個有序的子序列,每個子序列的長度為1,然后兩兩歸並,就能得到[n/2]個長度為2(或者1,落單的)的字序列,再不斷地兩兩歸並,直到得到一個長度為n的有序數組。

 

遞歸:

public static void main(String[] args) {
    int[] nums = {6, 5, 3, 8, 1, 7, 2, 9, 4};
    segment(nums, 0, nums.length - 1);
    System.out.println(Arrays.toString(nums));
}

/**
 * 遞歸切分待排
 *
 * @param nums  待切分數組
 * @param left  待切分最后第一個元素的索引
 * @param right 待切分數組最后一個元素的索引
 */
private static void segment(int[] nums, int left, int right) {
    if (left < right) {
        // 找出中間索引
        int center = (left + right) / 2;
        // 對左邊數組進行遞歸
        segment(nums, left, center);
        // 對右邊數組進行遞歸
        segment(nums, center + 1, right);
        // 合並
        merge(nums, left, center, right);
    }
}

/**
 * 兩兩歸並排好序的數組(2路歸並)
 *
 * @param nums   帶排序數組對象
 * @param left   左邊數組的第一個索引
 * @param center 左數組的最后一個索引,center + 1右數組的第一個索引
 * @param right  右數組的最后一個索引
 */
private static void merge(int[] nums, int left, int center, int right) {
    int[] tmpArray = new int[right - left + 1];
    int leftIndex = left;   //左數組第一個元素的索引
    int rightIndex = center + 1;   //右數組第一個元素索引
    int tmpIndex = 0;    //臨時數組索引

    // 把較小的數先移到新數組中
    while (leftIndex <= center && rightIndex <= right) {
        if (nums[leftIndex] <= nums[rightIndex]) {
            tmpArray[tmpIndex++] = nums[leftIndex++];
        } else {
            tmpArray[tmpIndex++] = nums[rightIndex++];
        }
    }

    // 把左邊剩余的數移入數組
    while (leftIndex <= center) {
        tmpArray[tmpIndex++] = nums[leftIndex++];
    }

    // 把右邊邊剩余的數移入數組
    while (rightIndex <= right) {
        tmpArray[tmpIndex++] = nums[rightIndex++];
    }

    // 把新數組中的數覆蓋nums數組
    /*for (int i = 0; i < tmpArray.length; i++) {
        nums[begin + i] = tmpArray[i];
    }*/
    //可以優化成下面的寫法
    System.arraycopy(tmpArray, 0, nums, left, tmpArray.length);
}

 

非遞歸:

public static void main(String[] args) {
    int[] nums = {6, 5, 3, 8, 1, 7, 2, 9, 4};
    for (int i = 1; i <= nums.length; i *= 2) {
        for (int j = 0; j + i <= nums.length; j += i * 2) {
            //Math.min 的目的是處理 整個待排序數組為奇數的情況
            merge(nums, j, j + i - 1, Math.min(j + 2 * i - 1, nums.length - 1));
        }
    }
    System.out.println(Arrays.toString(nums));
}

/**
 * 兩兩歸並排好序的數組(2路歸並)
 *
 * @param nums   帶排序數組對象
 * @param left   左邊數組的第一個索引
 * @param center 左數組的最后一個索引,center + 1右數組的第一個索引
 * @param right  右數組的最后一個索引
 */
private static void merge(int[] nums, int left, int center, int right) {
    int[] tmpArray = new int[right - left + 1];
    int leftIndex = left;   //左數組第一個元素的索引
    int rightIndex = center + 1;   //右數組第一個元素索引
    int tmpIndex = 0;    //臨時數組索引

    // 把較小的數先移到新數組中
    while (leftIndex <= center && rightIndex <= right) {
        if (nums[leftIndex] <= nums[rightIndex]) {
            tmpArray[tmpIndex++] = nums[leftIndex++];
        } else {
            tmpArray[tmpIndex++] = nums[rightIndex++];
        }
    }

    // 把左邊剩余的數移入數組
    while (leftIndex <= center) {
        tmpArray[tmpIndex++] = nums[leftIndex++];
    }

    // 把右邊邊剩余的數移入數組
    while (rightIndex <= right) {
        tmpArray[tmpIndex++] = nums[rightIndex++];
    }

    // 把新數組中的數覆蓋nums數組
    /*for (int i = 0; i < tmpArray.length; i++) {
        nums[begin + i] = tmpArray[i];
    }*/
    //可以優化成下面的寫法
    System.arraycopy(tmpArray, 0, nums, left, tmpArray.length);
}

 

參考鏈接:

[1].http://www.cnblogs.com/yulinfeng/p/7072163.html
[2].https://www.cnblogs.com/yulinfeng/p/7078661.html?utm_source=itdadao&utm_medium=referral
[3].https://blog.csdn.net/lll1204019292/article/details/52346199
[4].https://www.jianshu.com/p/39dd1d9b491d


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM