归并:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有
序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并的本质–空间换时间,通过申请一个额外数组存储中间变化,从而实现排序
归并排序核心步骤:
归并排序的特性总结:
. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。—外部排序首选
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
Merge(int array[], int left, int mid, int right, int extra[]) {
//向新数组放数
int i = left; // 左边区间的下标
int j = mid; // 右边区间的下标
int k = left; // extra 的下标---额外空间的下标
while (i < mid && j < right) {
//两个不同的区间可以理解为(0(i),mid)(j(mid),right)
if (array[i] <= array[j]) {
extra[k++] = array[i++];
}
else {
extra[k++] = array[j++];
}
}
while (i < mid) {
extra[k++] = array[i++];
}
while (j < right) {
extra[k++] = array[j++];
}
// 把 extra 的数据移回来
for (int x = left; x < right; x++) {
array[x] = extra[x];
}
}
// [left, right)
void MergeSort(int array[], int left, int right, int extra[]) {
结束:size == 1 ---左闭右开
if (right == left + 1) {
return;
}
// size == 0
if (right <= left) {
return;
}
// 1. 把数组平均分成两部分
int mid = left + (right - left) / 2;
// [left, mid)
// [mid, right)
// 2. 分治算法,排序左右两部分---递归的直到分成1个数字然后比较
__MergeSort(array, left, mid, extra);
__MergeSort(array, mid, right, extra);
// 3. 合并两个有序数组 [left, mid) [mid, right)
Merge(array, left, mid, right, extra);
}
void MergeSort(int array[], int size) {
int *extra = (int *)malloc(sizeof(int)* size);
__MergeSort(array, 0, size, extra);
free(extra);
}
void MergeSortNoR(int array[], int size) {
int* extra = (int *)malloc(sizeof(int)* size);
// i 的含义:i 个有序的数组 和另一个 i 个有序的数组进行合并
for (int i = 1; i < size; i = 2 * i) {
// 一层里需要多少次归并
// j 表示的是要合并的两个有序数组的左边数组的左边界
// i 是有序数组的长度
for (int j = 0; j < size; j = j + 2 * i) {
int left = j;
int mid = j + i;
if (mid >= size) {
// mid 越界,说明没有右边一个有序数组,不需要归并
continue;
}
int right = mid + i;
if (right > size) {
right = size;
}
Merge(array, left, mid, right, extra);
}
}
free(extra);
}