归并排序(二叉树理解递归 JAVA实现)
概念
- 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略
- 分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)
过程 图解
代码解释
- 归并排序需要一个额外的数组 shuZu(int[] a) 是创建额外数组的方法
- 排序块讲解
while (i <= mid && j <= right) {
if (a[i] > a[j]) {
b[g] = a[j];g++; j++;} else {
b[g] = a[i];g++;i++;}
判断当前组别left元素与mid+1元素的大小 将小的存放进b中 并将b的数组下标自增1 i自增1 确保当前分块的元素全部都被比较
while (i <= mid) {
b[g] = a[i];g++;i++;}
while (j <= right) {
b[g] = a[j];g++;j++;}
如果左右两组 一组存放完成 剩下的一组其他元素按它在自己块内的顺序依次存放在b中
g = 0;int tl = left;while (tl <= right) {
a[tl] = b[g];tl++;g++;}
由于我们运用了递归 接下来会重点讲这个过程
所以要边排边存 只存放排好序的 其他未发生排序变动的位置不发生改变
-
递归!!!!!!
public static void fenKuai(int[] a, int[] b, int left, int right) { if (left < right) { int mid = (left + right) / 2; fenKuai(a, b, left, mid); fenKuai(a, b, mid + 1, right); paiXu(a, b, left, mid, right); } }
过程比较繁琐 我们可以用二叉树来帮忙理解
如图 第一次进去的数字是 left 0 right 7 mid=7/2---3 java中取3 分为了0--3 4--7 这两组 先进行的是0--3这一组的遍历递归 进行 fenKuai(a, b, left, mid); 第二次进去的数字是 left 0 right 3 mid=3/2---1 分为了0--1 2--3这两组 先进行的是0--1这一组的遍历递归 进行 fenKuai(a, b, left, mid); 第二次进去的数字是 left 0 right 1 mid=1/2---0 因为0不小于0 所以 左支走完了 接着进行右支 进行的是2--3这一组的遍历递归 步骤同上 完成后 返回0--3》》》0--7》》4--7 接着进行的是4--7这一组的遍历递归 步骤同上 这就是二叉树理解递归的主要思想 二叉树的遍历
代码
import java.util.Arrays;
public class GuiBing {
public static void main(String[] args) {
int[] a = {555, 99, 518, 231, 159, 48, 23, 66};
int[] b = shuZu(a);
fenKuai(a, b, 0, a.length - 1);
System.out.println("排序后的结果为:" + Arrays.toString(a));
}
public static int[] shuZu(int[] a) {
int[] b = new int[a.length];
return b;
}
public static void fenKuai(int[] a, int[] b, int left, int right) {
if (left < right) {
System.out.println("计算mid" + "左边:" + left + "\t" + "右边:" + right);
int mid = (left + right) / 2;
System.out.println("初始" + mid);
fenKuai(a, b, left, mid);
System.out.println("1号操作:" + "左边:" + left + "\t" + "中间" + mid + "\t");
fenKuai(a, b, mid + 1, right);
System.out.println("2号操作:" + "右边:" + right + "\t" + "中间" + (mid + 1) + "\t");
System.out.println("左边:" + left + "\t" + "中间" + mid + "\t" + "右边:" + right + "进入函数");
paiXu(a, b, left, mid, right);
System.out.println("左边:" + left + "\t" + "中间" + mid + "\t" + "右边:" + right + "函数结束");
}
System.out.println("出去的步骤:左边:" + left + "\t" + "右边:" + right + "结果为:break!");
}
public static void paiXu(int[] a, int[] b, int left, int mid, int right) {
int i = left;
int j = mid + 1;
int g = 0;
while (i <= mid && j <= right) {
if (a[i] > a[j]) {
b[g] = a[j];
System.out.println("判断大小的值jj小" + a[j]);
g++;
j++;
} else {
b[g] = a[i];
System.out.println("判断大小的值ii大" + a[i]);
g++;
i++;
}
}
while (i <= mid) {
b[g] = a[i];
System.out.println("左边剩余值" + a[i]);
g++;
i++;
}
while (j <= right) {
b[g] = a[j];
System.out.println("右边剩余值" + a[j]);
g++;
j++;
}
System.out.println("左边:" + left + "\t" + "中间:" + mid + "\t" + "右边:" + right + "\t" + "个体分块结果为:" + Arrays.toString(b));
g = 0;
int tl = left;
while (tl <= right) {
a[tl] = b[g];
tl++;
g++;
}
System.out.println("左边:" + left + "\t" + "中间" + mid + "\t" + "右边:" + right + "\t" + "整体分块结果为:" + Arrays.toString(a));
}
}
运行结果:
计算mid左边:0 右边:7
初始3
计算mid左边:0 右边:3
初始1
计算mid左边:0 右边:1
初始0
出去的步骤:左边:0 右边:0结果为:break!
1号操作:左边:0 中间0
出去的步骤:左边:1 右边:1结果为:break!
2号操作:右边:1 中间1
左边:0 中间0 右边:1进入函数
判断大小的值jj小99
左边剩余值555
左边:0 中间:0 右边:1 个体分块结果为:[99, 555, 0, 0, 0, 0, 0, 0]
左边:0 中间0 右边:1 整体分块结果为:[99, 555, 518, 231, 159, 48, 23, 66]
左边:0 中间0 右边:1函数结束
出去的步骤:左边:0 右边:1结果为:break!
1号操作:左边:0 中间1
计算mid左边:2 右边:3
初始2
出去的步骤:左边:2 右边:2结果为:break!
1号操作:左边:2 中间2
出去的步骤:左边:3 右边:3结果为:break!
2号操作:右边:3 中间3
左边:2 中间2 右边:3进入函数
判断大小的值jj小231
左边剩余值518
左边:2 中间:2 右边:3 个体分块结果为:[231, 518, 0, 0, 0, 0, 0, 0]
左边:2 中间2 右边:3 整体分块结果为:[99, 555, 231, 518, 159, 48, 23, 66]
左边:2 中间2 右边:3函数结束
出去的步骤:左边:2 右边:3结果为:break!
2号操作:右边:3 中间2
左边:0 中间1 右边:3进入函数
判断大小的值ii大99
判断大小的值jj小231
判断大小的值jj小518
左边剩余值555
左边:0 中间:1 右边:3 个体分块结果为:[99, 231, 518, 555, 0, 0, 0, 0]
左边:0 中间1 右边:3 整体分块结果为:[99, 231, 518, 555, 159, 48, 23, 66]
左边:0 中间1 右边:3函数结束
出去的步骤:左边:0 右边:3结果为:break!
1号操作:左边:0 中间3
计算mid左边:4 右边:7
初始5
计算mid左边:4 右边:5
初始4
出去的步骤:左边:4 右边:4结果为:break!
1号操作:左边:4 中间4
出去的步骤:左边:5 右边:5结果为:break!
2号操作:右边:5 中间5
左边:4 中间4 右边:5进入函数
判断大小的值jj小48
左边剩余值159
左边:4 中间:4 右边:5 个体分块结果为:[48, 159, 518, 555, 0, 0, 0, 0]
左边:4 中间4 右边:5 整体分块结果为:[99, 231, 518, 555, 48, 159, 23, 66]
左边:4 中间4 右边:5函数结束
出去的步骤:左边:4 右边:5结果为:break!
1号操作:左边:4 中间5
计算mid左边:6 右边:7
初始6
出去的步骤:左边:6 右边:6结果为:break!
1号操作:左边:6 中间6
出去的步骤:左边:7 右边:7结果为:break!
2号操作:右边:7 中间7
左边:6 中间6 右边:7进入函数
判断大小的值ii大23
右边剩余值66
左边:6 中间:6 右边:7 个体分块结果为:[23, 66, 518, 555, 0, 0, 0, 0]
左边:6 中间6 右边:7 整体分块结果为:[99, 231, 518, 555, 48, 159, 23, 66]
左边:6 中间6 右边:7函数结束
出去的步骤:左边:6 右边:7结果为:break!
2号操作:右边:7 中间6
左边:4 中间5 右边:7进入函数
判断大小的值jj小23
判断大小的值ii大48
判断大小的值jj小66
左边剩余值159
左边:4 中间:5 右边:7 个体分块结果为:[23, 48, 66, 159, 0, 0, 0, 0]
左边:4 中间5 右边:7 整体分块结果为:[99, 231, 518, 555, 23, 48, 66, 159]
左边:4 中间5 右边:7函数结束
出去的步骤:左边:4 右边:7结果为:break!
2号操作:右边:7 中间4
左边:0 中间3 右边:7进入函数
判断大小的值jj小23
判断大小的值jj小48
判断大小的值jj小66
判断大小的值ii大99
判断大小的值jj小159
左边剩余值231
左边剩余值518
左边剩余值555
左边:0 中间:3 右边:7 个体分块结果为:[23, 48, 66, 99, 159, 231, 518, 555]
左边:0 中间3 右边:7 整体分块结果为:[23, 48, 66, 99, 159, 231, 518, 555]
左边:0 中间3 右边:7函数结束
出去的步骤:左边:0 右边:7结果为:break!
排序后的结果为:[23, 48, 66, 99, 159, 231, 518, 555]
进程已结束,退出代码0