归并排序(Merge Sort)
视频教程:【十大排序算法】用可视化动画讲归并排序
1、介绍
归并排序是利用归并的思想实现的排序算法,该算法采用经典的分治策略(分治法先将一个大的问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
2、算法步骤
1、将一组数据尽可能的拆分成两个元素数量相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止;
2、将相邻的两个子组进行合并成一个有序的大组;
3、不断的重复步骤2,直到最终只有一个组为止;
图解:
3、代码实现
1、Java版代码
package sort;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int arr[] = { 88, 85, 21, 30, 90, 93, 48, 22 };
int temp[] = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, temp);
System.out.println("排序后:");
System.out.println(Arrays.toString(arr));
}
/**
* 归并排序,分+合
*
* @param arr 排序的原始数组
* @param left 左边界index
* @param right 右边界index
* @param temp 临时的数组
*/
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
// 算出中间值index
int mid = left + (right - left) / 2;
// 分
System.out.println("向左递归:" + left + ":" + mid);
mergeSort(arr, left, mid, temp);
// 分
System.out.println("向右递归:" + (mid + 1) + ":" + right);
mergeSort(arr, mid + 1, right, temp);
// 合并
merge(arr, left, mid, right, temp);
}
}
/**
* 合并的方法:一次对两个子组合并,合并成一个有序的大组
*
* @param arr 排序的原始数组
* @param left 左边界index
* @param mid 中间值index
* @param right 右边界index
* @param temp 临时的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
System.out.println("开始合并:" + left + ":" + right + ":" + mid);
int L = left; // 左比较值下标
int R = mid + 1; // 右比较值下标
int tempIndex = 0; // 临时数组temp的下标
// while循环1:比较下标为L和R的值,将小的值插入到temp中
while (L <= mid && R <= right) {
// 左比较值小于等于右比较值,将左比较值插入到temp中
if (arr[L] <= arr[R]) {
temp[tempIndex] = arr[L];
tempIndex++;
L++;
}
// 右比较值小于左比较值,将右比较值插入到temp中
if (arr[L] > arr[R]) {
temp[tempIndex] = arr[R];
tempIndex++;
R++;
}
}
// while循环2:将左边的值全部插入到temp中
while (L <= mid) {
temp[tempIndex] = arr[L];
tempIndex++;
L++;
}
// while循环3:将右边的值全部插入到temp中
while (R <= right) {
temp[tempIndex] = arr[R];
tempIndex++;
R++;
}
System.out.println("当前左右边界:" + left + "," + right + ",插入完的temp:" + Arrays.toString(temp));
// while循环4:将temp中的值插回到原数组arr中
int index = 0;
while (left <= right) {
arr[left++] = temp[index++];
}
}
}
2、C语言版代码
#include<stdio.h>
#define len 8
/**
* 归并排序,分+合
*
* @param arr 排序的原始数组
* @param left 左边界index
* @param right 右边界index
* @param temp 临时的数组
*/
void mergeSort(int arr[], int left, int right, int temp[]) {
if (left < right) {
// 算出中间值index
int mid = left + (right - left) / 2;
// 分
printf("向左递归:%d:%d\n",left,right);
mergeSort(arr, left, mid, temp);
// 分
printf("向右递归:%d:%d\n",(mid + 1),right);
mergeSort(arr, mid + 1, right, temp);
// 合并
merge(arr, left, mid, right, temp);
}
}
/**
* 合并的方法:一次对两个子组合并,合并成一个有序的大组
*
* @param arr 排序的原始数组
* @param left 左边界index
* @param mid 中间值index
* @param right 右边界index
* @param temp 临时的数组
*/
void merge(int arr[], int left, int mid, int right, int temp[]) {
printf("开始合并:%d:%d:%d\n",left,right,mid);
int L = left; // 左比较值下标
int R = mid + 1; // 右比较值下标
int tempIndex = 0; // 临时数组temp的下标
// while循环1:比较下标为L和R的值,将小的值插入到temp中
while (L <= mid && R <= right) {
// 左比较值小于等于右比较值,将左比较值插入到temp中
if (arr[L] <= arr[R]) {
temp[tempIndex] = arr[L];
tempIndex++;
L++;
}
// 右比较值小于左比较值,将右比较值插入到temp中
if (arr[L] > arr[R]) {
temp[tempIndex] = arr[R];
tempIndex++;
R++;
}
}
// while循环2:将左边的值全部插入到temp中
while (L <= mid) {
temp[tempIndex] = arr[L];
tempIndex++;
L++;
}
// while循环3:将右边的值全部插入到temp中
while (R <= right) {
temp[tempIndex] = arr[R];
tempIndex++;
R++;
}
printf("当前左右边界:%d:%d,插入完的temp:",left,right);
// 输出temp
int i;
for(i=0; i < len; i++) {
printf("%d ,", arr[i]);
}
printf("\n");
// while循环4:将temp中的值插回到arr中
int index = 0;
while (left <= right) {
arr[left++] = temp[index++];
}
}
int main() {
int arr[] = {88, 85, 21, 30, 90, 93, 48, 22};
int temp[len];
int i;
mergeSort(arr, 0, len - 1, temp);
printf("\n排序后:\n");
for(i=0; i < len; i++) {
printf("%d ", arr[i]);
}
}
4、时空复杂度
时间复杂度:O(nlogn)
初学者掌握代码即可。
需要了解的同学可以看这篇博客:详谈归并排序时间复杂度过程推导----软考
空间复杂度:O(n)
排序过程中用到了和原数组大小相同的临时数组
5、稳定性
归并排序是稳定的排序算法
稳定性:排序前两个值相等的元素的前后位置顺序和排序后它们两个的前后位置顺序相同
6、结束语
打开微信扫描小程序码体验归并排序动画演示: