該命題已有無數解釋,備份修改后的代碼
平均時間復雜度: 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