1、算法思想
歸併排序的基本思想是,把大的數組劃分成兩個子數組,然後分別對這兩個子數組分別進行排序,之後在把這兩個排好序的子數組合併成一個有序的數組。由於兩個小的數組都是有序的,所以在合併的時候是很快的。
通過遞歸的方式將大的數組一直分割,直到數組的大小爲 1,此時只有一個元素,那麼該數組就是有序的了,之後再把兩個數組大小爲1的合併成一個大小爲2的,再把兩個大小爲2的合併成4的 …… 直到全部小的數組合並起來。
爲方便理解我還準備了動圖:
2、代碼實現
遞歸版本:
void mergeSort(int[] arr, int left, int right) {
// 如果 left == right,表示數組只有一個元素,則不用遞歸排序
if (left < right) {
// 把大的數組分隔成兩個數組
int mid = (left + right) / 2;
// 對左半部分進行排序
arr = mergeSort(arr, left, mid);
// 對右半部分進行排序
arr = mergeSort(arr, mid + 1, right);
//進行合併
merge(arr, left, mid, right);
}
return arr;
}
// 合併函數,把兩個有序的數組合並起來
// arr[left..mif]表示一個數組,arr[mid+1 .. right]表示一個數組
void merge(int[] arr, int left, int mid, int right) {
//先用一個臨時數組把他們合併彙總起來
int[] a = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
a[k++] = arr[i++];
} else {
a[k++] = arr[j++];
}
}
while(i <= mid) a[k++] = arr[i++];
while(j <= right) a[k++] = arr[j++];
// 把臨時數組複製到原數組
for (i = 0; i < k; i++) {
arr[left++] = a[i];
}
}
非遞歸版本:
void mergeSort(int* arr,int length) {
int n = length;
// 子數組的大小分別爲1,2,4,8...
// 剛開始合併的數組大小是1,接着是2,接着4....
for (int i = 1; i < n; i += i) {
//進行數組進行劃分
int left = 0;
int mid = left + i - 1;
int right = mid + i;
//進行合併,對數組大小爲 i 的數組進行兩兩合併
while (right < n) {
// 合併函數和遞歸式的合併函數一樣
merge(arr, left, mid, right);
left = right + 1;
mid = left + i - 1;
right = mid + i;
}
// 還有一些被遺漏的數組沒合併,千萬別忘了
// 因爲不可能每個字數組的大小都剛好爲 i
if (left < n && mid < n) {
merge(arr, left, mid, n - 1);
}
}
return arr;
}
}
3、算法分析
- 時間複雜度:O(nlogn)
- 空間複雜度:O(n)
- 穩定性:穩定的
- 非原地排序