归并排序(Merge Sort)
基本思想
核心是分治,就是把一个复杂的问题分成两个或多个相同或相似的子问题,然后把子问题分成更小的子问题,直到子问题可以简单的直接求解,最原问题的解就是子问题解的合并。归并排序将分治的思想体现得淋漓尽致。
代码实例
package com.yq.javaSEPrj.algorithm;
import java.util.Arrays;
public class MergeSort {
/**
* 主题函数
*/
public static void sort(int[] A, int lo, int hi) {
// 判断是否只剩下最后一个元素
if (lo >= hi) return;
// 从中间将数组分成两个部分
int mid = lo + (hi - lo) / 2;
// 分别递归地将左右两半排好序
sort(A, lo, mid);
sort(A, mid + 1, hi);
// 将排好序的左右两半合并
merge(A, lo, mid, hi);
System.out.println(Arrays.toString(A));
}
/**
* 归并操作
*/
public static void merge(int[] nums, int lo, int mid, int hi) {
// 复制一份原来的数组
int[] copy = nums.clone();
// 定义一个 k 指针表示从什么位置开始修改原来的数组,i 指针表示左半边的起始位置,j 表示右半边的起始位置
int k = lo, i = lo, j = mid + 1;
while (k <= hi) {
if (i > mid) {
nums[k++] = copy[j++];
} else if (j > hi) {
nums[k++] = copy[i++];
} else if (copy[j] < copy[i]) {
nums[k++] = copy[j++];
} else {
nums[k++] = copy[i++];
}
}
}
// 测试
public static void main(String[] args) {
int[] arr = {2, 1, 7, 9, 5, 8};
sort(arr,0,arr.length -1 );
}
}
实现
一开始先把数组从中间划分成两个子数组,一直递归地把子数组划分成更小的子数组,直到子数组里面只有一个元素,才开始排序。
排序的方法就是按照大小顺序合并两个元素,接着依次按照递归的返回顺序,不断地合并排好序的子数组,直到最后把整个数组的顺序排好。
其中,While 语句比较,一共可能会出现四种情况。
-
左半边的数都处理完毕,只剩下右半边的数,只需要将右半边的数逐个拷贝过去。
-
右半边的数都处理完毕,只剩下左半边的数,只需要将左半边的数逐个拷贝过去就好。
-
右边的数小于左边的数,将右边的数拷贝到合适的位置,j 指针往前移动一位。
-
左边的数小于右边的数,将左边的数拷贝到合适的位置,i 指针往前移动一位。
例题分析
例题:利用归并排序算法对数组 [2, 1, 7, 9, 5, 8] 进行排序。
解题思路
首先不断地对数组进行切分,直到各个子数组里只包含一个元素。
接下来递归地按照大小顺序合并切分开的子数组,递归的顺序和二叉树里的前向遍历类似。
- 合并 [2] 和 [1] 为 [1, 2]。
- 子数组 [1, 2] 和 [7] 合并。
- 右边,合并 [9] 和 [5]。
- 然后合并 [5, 9] 和 [8]。
- 最后合并 [1, 2, 7] 和 [5, 8, 9] 成 [1, 2, 5, 8, 9],就可以把整个数组排好序了。
- 合并数组 [1, 2, 7] 和 [5, 8, 9] 的操作步骤如下。
把数组 [1, 2, 7] 用 L 表示,[5, 8, 9] 用 R 表示。 合并的时候,开辟分配一个新数组 T
保存结果,数组大小应该是两个子数组长度的总和 然后下标 i、j、k 分别指向每个数组的起始点。 接下来,比较下标i和j所指向的元素 L[i]
和 R[j],按照大小顺序放入到下标 k 指向的地方,1 小于 5。 移动 i 和 k,继续比较 L[i] 和 R[j],2 比 5 小。
i 和 k 继续往前移动,5 比 7 小。 移动 j 和 k,继续比较 L[i] 和 R[j],7 比 8 小。
这时候,左边的数组已经处理完毕,直接将右边数组剩余的元素放到结果数组里就好。 合并之所以能成功,先决条件必须是两个子数组都已经分别排好序了。
收录自谷歌大佬