归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
基本思想
说明:
可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程。
合并相邻有序子序列:
再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤
代码展示
#include <stdio.h>
//治---相当于合并的操作
void merge(int left, int mid, int right, int arr[], int temp[]) {// arr[]实际的数组,temp作为临时数组 存放排序好的数据
int i = left; // 初始化i, 左边有序序列的初始索引
int j = mid + 1;//初始化j, 右边有序序列的初始索引
int t = 0;//作为temp数组的索引
//(一)
//先把左右两边(有序)的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕为止
while (i <= mid && j <= right) {
//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
//即将左边的当前元素,填充到 temp数组
//然后 t++, i++
if(arr[i] < arr[j]) {
temp[t] = arr[i];
t++;
i++;
}
else {//反之,将右边有序序列的当前元素,填充到temp数组
temp[t] = arr[j];
t++;
j++;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到temp
//左边的有序序列还有剩余的元素,就全部填充到temp
while (i <= mid) {
temp[t] = arr[i];
i++;
t++;
}
//这是右边可能有剩余元素
while (j <= right) {
temp[t] = arr[j];
j++;
t++;
}
//(三)
//将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left;
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
//分---相当于分开的操作
void mergeSort(int left, int right, int arr[], int temp[]) {
if (left < right) {
int mid = (left + right) / 2;
//向左边进行递归
mergeSort(left, mid, arr, temp);
//向右边进行递归
mergeSort(mid + 1, right, arr, temp);
//都分开的操作后进行治
merge(left, mid, right, arr, temp);
}
}
int main() {
int arr[] = {8,4,5,7,1,3,6,2};
int temp[8];
int len = sizeof(arr) / sizeof(arr[0]);
mergeSort(0, len - 1, arr, temp);
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
return 0;
}
运行结果
时间复杂度
归并排序的时间复杂度是O(nlogn)–log是以2为底 ,最坏的情形也是O(nlogn)。归并排序的稳定性是:稳定的。