归并排序
归并排序,主体思想还是分而治之,将一整块完整的内容拆成几个小块来实现,最后将结果汇总成一个大的结果,就实现了需要的效果
归并的玩法就是先拆,拆完保证左边也有序,右边也有序,再想怎么把两个有序数组合并
所以归并的问题可以总结为两个问题,一是怎么拆,二是怎么合
怎么拆?
- 对于排序来讲,如果只有两个元素,拆为左右各一个元素,那么左右两组的顺序就特别好比较
- 我们将任务拆到一个元素的级别,就能保证左边也有序,右边也有序(废话,自己和自己比,当然有序)
怎么合?
- 其实归并算法的难就难在理解合并上,合并的过程中需要申请一个辅助空间来协助计算,整体上来说就是同时在左右两个分支数组的起始位置设置一个索引,某一处的数较小,就按顺序存入辅助空间中,等所有数全部存入辅助空间后,一次性将原数组对应的位置进行覆盖
示例
归并开始的前提就是左右两侧的数组已经排序完成,举例如图
-
初始情况
-
比较开始,左索引与右索引的数开始比较,发现右索引的数较小,将右索引数放到辅助数组,辅助索引和右索引自增
- 第二次比较,会发现左索引的数比较小,OK,左索引对应的3加入辅助数组,左索引和辅助索引自增
- 继续比较,4比较小,将4存入,左索引和辅助索引自增
- 再比一次,6比较小,6进去
- 再比较,7比较小,扔进去 ,两个索引自增
- 到了这里要注意,右侧的索引已经越界,说明右侧的数据已经全部在辅助数组里,并且右侧所有的数是小于我左索引指向的值的,也就意味着左边多出来的所有的数都是大于右侧所有的数的,直接将多出来的数补进辅助索引即可
- 到了这里,辅助数组就已经计算完顺序了,接下来,只要把辅助数组里的数据按顺序写到原数组里就行了
代码实现(java)
//分拆方法
private void process (int[] arr, int start, int end) {
if (start == end) {
return;
}
int mid = start + ((start - end) >> 1);
//从左到中间有序
process(arr, start, mid);
//从中点到右有序
process(arr, mid + 1, end);
//合并左右
merge(arr, start, end, mid);
}
//合并方法
private void merge (int[] arr, int left, int right, int mid) {
//创建辅助数组
int[] tmp = new int[right - left + 1];
int i = 0;
int leftIndex = left;
int rightIndex = mid + 1;
//只要没有任意指针越界,就谁小复制到新数组里
while (leftIndex <= mid && rightIndex <= right) {
tmp[i++] = arr[leftIndex] < arr[rightIndex] ? arr[leftIndex++] : arr[rightIndex++];
}
//把另一个没写完的数组写完
while (leftIndex <= mid) {
tmp[i++] = arr[leftIndex++];
}
while (rightIndex <= right) {
tmp[i++] = arr[rightIndex++];
}
//写回去
for (int i1 = 0; i1 < tmp.length; i1++) {
//注意起点
arr[left + i1] = tmp[i1];
}
}