年纪大了,学习以前的递归,总是不能细想到每一步的过程,通过在纸上推演与思考,终于弄明白了,其实弄明白后,一切就很简单了。
归并排序及代码如下,来自于实验楼,也可以自行百度。
归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
排序过程:
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
- 重复步骤 3 直到某一指针到达序列尾
- 将另一序列剩下的所有元素直接复制到合并序列尾
import java.util.Arrays;
public class MergeSort {
public static void mergeSort(int[] arrays, int left, int right) {
// 如果数组还可以拆分
if (left < right) {
//数组的中间位置
int middle = (left + right) / 2;
//拆分左边数组
mergeSort(arrays, left, middle);
//拆分右边数组
mergeSort(arrays, middle + 1, right);
//合并
merge(arrays, left, middle, right);
}
}
/**
* 合并数组
*/
public static void merge(int[] arr, int left, int middle, int right) {
//申请合并空间 大小为两个已经排序序列之和
int[] temp = new int[right - left + 1];
//i 和 j为两个已经排好序的数组的起始位置
int i = left;
int j = middle + 1;
int k = 0;
//排序
while (i <= middle && j <= right) {
//将比较小的数组放入合并空间
if (arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
//将左边剩余元素写入合并空间
while (i <= middle) {
temp[k++] = arr[i++];
}
//将右边剩余的元素写入合并空间
while (j <= right) {
temp[k++] = arr[j++];
}
//将排序后的数组写回原来的数组
for (int l = 0; l < temp.length; l++) {
arr[l + left] = temp[l];
}
}
public static void main(String[] args) {
int[] ints = {5, 3, 4, 1, 2};
mergeSort(ints,0,ints.length-1);
System.out.println(Arrays.toString(ints));
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
下面的代码是自己为了推演,加了很多显示语句
//归并排序解析
//归并排序先用递归的思路把一个数组两两分成小组,小组比较大小后,再合并成大组,合并的过程也是递归的思路
//在合并过程中,用了一个临时数组作为中转
import java.util.Arrays;
public class MergeSort
{
//用testnum表示递归的次数
public static int testnum=1;
//left,right为arrays数组的下标
public static void mergeSort(int[] arrays,int left,int right)
{
if(left<right)
{
System.out.print("第"+testnum+"次mergeSort运算 ");
System.out.print("当前mergeSort中的arrays数组为: "+Arrays.toString(arrays));
System.out.print(" left为 "+left+",right为 "+right);
int middle=(left+right)/2;
System.out.println(" 此时middle为:"+middle);
testnum++;
//这是一个递归过程,如果数组是偶数个元素,方便理解,但如果数组是奇数个元素,就稍微有点抽象了
//为何这么说呢?因为涉及三点:1、数组的长度为n,但数组元素的下标是从0开始的,所以,数组的最后一个元素,下标为n-1;
//2、"/"运算中,如果取int型,是直接取商,比如10/3=3;
//3、如果不用纸笔推算,只在大脑中想象,偶数个数组元素容易理解这个递归过程,最后肯定是两两分组的嘛,
//但奇数个元素的话,总是想最终还多一个元素怎么办啊。
//鉴于以上几个方面,让我想起了算法的经典题,汉诺塔问题,也是一个经典的递归算法
//递归只所以抽象,在于部分人在思考的过程中,总想细化到每一个步骤,可计算机的递归比人的思维更快,直接把递归结果整出来了
//而我要在大脑中重复地去推演递归,推演每一个过程,不然总是不放心,考虑到这种情况,我觉得必须把每一个步骤显示出来
//这样彻底解决递归这个抽象的问题,不在脑海中存下疙瘩
//以两种数组为例即可,一个数组为偶数长度,另一个数组为奇数长度,但在推演的过程中,可以举多个数组,比如{0,1},{1,2,3}
//逐步去体会递归
//下面三行代码即为整个代码的核心,先把左边部分分拆,再把右边部分分拆,再合并
//成为要注意的是,递归类似于树图,下面的三行代码是三个行为,这三个行为处于树图的同一级,类似于兄弟节点
//而每个行为都会继续递归形成本节点下的子节点,只有等本节点下的全部执行完,才会继续向兄弟节点执行
mergeSort(arrays,left,middle);
mergeSort(arrays,middle+1,right);
//合并函数
merge(arrays,left,middle,right);
}
else
{
System.out.println("mergeSort中传入的参数left并不小于right,条件不成立,返回上一层空间的兄弟节点执行**************");
}
}
//用numinmerge表示合并的次数;merge函数也是非常的抽象,尤其是其中的while循环
public static int numinmerge=1;
public static void merge(int[] arr,int left,int middle,int right)
{
System.out.print("这是第"+numinmerge+"次merge运算");
numinmerge++;
int[] temp=new int[right-left+1];
System.out.println("right-left+1值代表新建临时数组的长度,值为"+(right-left+1)+"; left:"+left+" middle:"+middle+" right:"+right);
//注意,下面的代码中,是对i j k 操作,而left middle right的值都没有变化
int i=left;
//为什么让j等于middle+1
//****&&&通过在纸上扮演与观察结果,发现,下面的三个while语句才是关键,j=middle+1代表后半段数组,与前半段分别比较,小的就放入临时数组
int j=middle+1;
int k=0;
int numinwhileONE=1;
while(i<=middle&&j<=right)
{
System.out.println("while(i<=middle&&j<=right)循环才开始, i 初始值为left,现为:"+i+" j 初始值为middle+1,现为:"+j+" k 初始值为0,现为:"+k);
System.out.println("第"+numinwhileONE+"次在while中运算");
numinwhileONE++;
if(arr[i]<arr[j])
{
temp[k++]=arr[i++];
}
else
{
temp[k++]=arr[j++];
}
System.out.println("while(i<=middle&&j<=right)循环一次结束,几个参数的值, i: "+i+" j: "+j+" k: "+k);
}
System.out.println("left middle right值在merge中不是变的,分别为 left:"+left+" middle:"+middle+" right:"+right);
//下面两个while必然有一个成立
while(i<=middle)
{
temp[k++]=arr[i++];
System.out.println("while(i<=middle)循环一次,这个语句中改变了k与i的值, k: "+k+" i: "+i);
}
while(j<=right)
{
temp[k++]=arr[j++];
System.out.println("while(j<=right)循环一次,这个语句中改变了k与i的值, k: "+k+" j: "+j);
}
//把temp中已经排好大小顺序的数赋值到原数组中,从下标left开始
for(int l=0;l<temp.length;l++)
{
arr[l+left]=temp[l];
}
System.out.println(Arrays.toString(temp));
System.out.println("本级节点merge函数运算完成,改变了原数组中的数字顺序,返回上级节点的mergeSort()运算 ");
}
public static void main(String[] args)
{
int[] ints={7,4,6};
mergeSort(ints, 0, ints.length-1);
System.out.println(Arrays.toString(ints));
}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
弄明白了就舒服了,自己在此做个标记