java中归关排序算法自我推演

年纪大了,学习以前的递归,总是不能细想到每一步的过程,通过在纸上推演与思考,终于弄明白了,其实弄明白后,一切就很简单了。

归并排序及代码如下,来自于实验楼,也可以自行百度。

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

排序过程:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤 3 直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

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));
    }
}

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

弄明白了就舒服了,自己在此做个标记

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qlovepeng1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值