什么是归并排序
归并排序的核心思想还是蛮简单的。如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。归并排序使用的就是分治思想。分治,顾名思义,就是分而治之,将一个大问题分解成小的子问题来解决。小的子问题解决了,大问题也就解决了。分治算法一般都是用递归来实现的。分治是一种解决问题的处理思想,递归(传递—>回归)是一种编程技巧,这两者并不冲突。
从编码的角度分析归并排序
刚开始接触归并排序的时候,可能会有这么一个疑惑。利用递归的方式将大数组不断拆分成小数组,然后将小数组合并起来,最后就能实现排序呢?这里不要去想递归的整个过程,更不要想去搞清递归的每一次执行结果。这样会陷入一个思维误区,人的思维方式和计算机处理问题的方式是不一样的。
这里说明一下几个思考点:
- 递归出口不只是意味递归到此结束,还说明数组被拆分到最小数组时的数组长度为1,对于一个数来说它是绝对有序的!
- 什么时候第一次数组合并?当然是数组被拆分到最小数组(长度为1)时合并,此时左边的有序数组长度为1右边的数组长度为1。我们只需要将小的数放在前面即可,最后就是一个长度为2的有序数组
- 以此类推,递归算法从传递(拆分数组)到回归(合并有序数组)
package com.learn.algo;
import java.util.Arrays;
public class Sort {
public static void main(String[] args) {
int[] numbers = new int[] {1,2,3,1,2,5,14,21,11,20,95,100,8,9};
System.out.println(Arrays.toString(numbers));
mergeSort(numbers,0,numbers.length - 1);
System.out.println(Arrays.toString(numbers));
}
/**
* 归并排序
* @param numbers 待排序数组
* @param startIndex
* @param endIndex
*/
public static void mergeSort(int[] numbers, int startIndex, int endIndex) {
//递归出口
if (startIndex >= endIndex) return;
//计算数组的中间元素下标
int middleIndex = (endIndex + startIndex) / 2;
//对左边进行排序
mergeSort(numbers, startIndex, middleIndex);
//对右边进行排序
mergeSort(numbers, middleIndex + 1, endIndex);
//合并左右有序数组
merge(numbers, startIndex, middleIndex, endIndex);
}
/**
* 合并两个有序数组
* @param numbers
* @param startIndex
* @param middleIndex
* @param endIndex
*/
private static void merge(int numbers[], int startIndex, int middleIndex, int endIndex) {
//定义一个缓存数组
int[] buffer = new int[endIndex - startIndex + 1];
//从左边开始比较,左边的小于等于右边的就排在前面,这样原有数组中元素大小的相对位置不会改变,该算法就为稳定的排序算法
//左边有序数组number[startIndex]->number[middleIndex]
//右边有序数组number[middleIndex + 1]->number[endIndex]
//while中的条件是为了保证左边或右边的有序数组至少有一个遍历完
int l = startIndex, r = middleIndex + 1, i = 0;
while (l <= middleIndex && r <= endIndex) {
if (numbers[l] <= numbers[r]) {
buffer[i++] = numbers[l++];
} else {
buffer[i++] = numbers[r++];
}
}
//如果左边的有序数组未遍历完则直接提取出左边的元素
while (l <= middleIndex) {
buffer[i++] = numbers[l++];
}
//如果右边的有序数组未遍历完则直接提取出左边的元素
while (r <= endIndex) {
buffer[i++] = numbers[r++];
}
//赋值给原始数组
System.arraycopy(buffer, 0, numbers, startIndex, buffer.length);
}
}