首页 > 其他分享 >归并排序

归并排序

时间:2022-08-22 22:46:52浏览次数:46  
标签:归并 步长 int arr step mergeStep 数组 排序

package class08;

/**
 * 归并排序
 * <p>
 * 时间复杂度:O(N*logN)
 * 描述:
 * 1.数组长度N,步长step去追N,1变2,2变4,4变8。。。
 * 所以step变了几次?logN次。
 * 2.step每变一次的,时间复杂度是多少呢?
 * 每一次左组和右组merge,左组和右组merge,左组和右组merge。。。
 * 时间复杂度为:O(N)
 * <p>
 * 所以总的时间复杂度为O(N*logN)。
 */
public class Code02_MergeSort {
    //1.递归方式
    public static void mergeSort1(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        process(arr, 0, arr.length - 1);
    }

    /**
     * 递归,使需要被排序的数组arr,左半边有序,再使右半边有序。再合并。
     *
     * @param arr 需要排序的int类型的数组
     * @param L   arr的最小的索引
     * @param R   arr的最大的索引
     */
    public static void process(int[] arr, int L, int R) {
        if (L == R) {//如果只有一个数,那么就是有序的,直接返回。
            return;
        }
        //mid等于(L+R)/2。因为当L和R很大时,这种写法会越界。所以使用下边的,位运算的写法,不会越界。
        int mid = L + ((R - L) >> 1);
        process(arr, L, mid);//使整个数组的左半边有序
        process(arr, mid + 1, R);//使整个数组的右半边有序
        merge(arr, L, mid, R);//将两个各自有序的数组,进行合并。
    }

    /**
     * @param arr 需要被排序的数组
     * @param L   数组的最小的索引
     * @param M   数组的中间位置的索引
     * @param R   数组的最大的索引
     */
    private static void merge(int[] arr, int L, int M, int R) {
        //定义一个辅助数组help,长度等于L到R的长度。
        int[] help = new int[R - L + 1];
        int i = 0;//定义辅助数组的指针。
        int p1 = L;//定义左半边数组的指针p1,初始值是L。
        int p2 = M + 1;//定义右半边数组的指针p2,初始值是M+1。
        //只要p1没有越界,并且p2没有越界。
        while (p1 <= M && p2 <= R) {
            //如果左半边数组中,指针p1所指的数字,小于等于,右半边数组中,指针p2所指的数字。
            //那么将arr[p1]的值,赋值给help[i]。p1++,i++。

            //反之,如果左半边数组中,指针p1所指的数字,大于,右半边数组中,指针p2所指的数字。
            //那么将数组arr[p2]的值,赋值给help[i]。p2++,i++。
            help[i++] = (arr[p1] <= arr[p2]) ? arr[p1++] : arr[p2++];
        }
        //当程序走到这里,即跳出第一个while循环时,说明p1和p2,有且只有一个越界了。
        //当p1没有越界时
        while (p1 <= M) {
            //将arr[p1]的值,赋值给help[i]。p1++,i++。
            //也就是当右半边数组,p2越界时,将左半边数组剩余的所有元素,照抄到辅助数组help中。
//            help[i++] = arr[p1++];
            help[i] = arr[p1];
            p1++;
            i++;
        }
        //当p2没有越界时
        while (p2 <= R) {
            //将arr[p2]的值,赋值给help[i]。p2++,i++。
            //也就是当左半边数组,p1越界时,将右半边数组剩余的所有元素,照抄到辅助数组help中。
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            //将辅助数组help中的每一个元素,按照索引位置,原封不动地,覆盖掉原来的数组arr。
            arr[L + i] = help[i];
        }
    }

    //2.非递归方式。(迭代的方式)
    public static void mergeSort2(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        int step = 1;//初始化步长为1
        int N = arr.length;
        //只要步长小于数组的长度,就循环下去。
        //是小于,而不是小于等于。为什么没有等于?
        //因为当步长step等于数组长度N的时候,整体的左组已经有序了,右组为空。
        //既然右组为空,那么就没必要费那一下劲了。即把右组的空,拿出来,和左组合并,并且排序。(即merge和sort)
        while (step < N) {
            int L = 0;//L代表每一个左组的第一个索引。
            //这个内层while循环的作用是:
            //每将step乘2一次,将左组和右组调一遍。再 每将step乘2一次,再 将左组和右组调一遍。循环。
            while (L < N) {
                //1.先看左组,即从L到M范围上。
                int M = 0;//每一个左组的最后一个索引。
                //L到N-1上,一共有几个数呢?(N-1)-L再+1。
                //((N - 1) - L) + 1 >= step
                if (N - L >= step) {//如果L到N-1上,数字的个数,大于等于步长step,则M等于L+step-1。
                    M = L + step - 1;
                } else {
                    M = N - 1;//否则,M等于整个数组arr的最后一个索引,即N-1。也就是说,L到N-1的数字的个数不够step个了,那么M就等于N-1了。
                }
                //上方代码的三元写法:M = N - L >= step ? (L + step - 2) : (N - 1);

                //如果左组的最后一个索引M,等于数组arr的最后一个索引N-1。
                //则表示右组为空。所以此时break。这个break,是跳出内层循环,直接去增加步长。即step*=2;
                if (M == N - 1) {
                    break;
                }
                //至此左组有序了,即L到M上有序了。
                //=======================================================================================
                //2.再看右组,即M+1到N-1范围上。
                int R = 0;//R表示右组的最后一个索引。
                //R可能等于M+1 + step - 1,也可能等于数组arr的最后一个索引,即N-1。
                //(N - 1) - (M + 1) + 1 >= step
                if (N - 1 - M >= step) {//如果M+1到N-1上,数字的个数,大于等于步长step,则R=M+step。
                    R = M + step;//R = M + 1 + step - 1;
                } else {
                    R = N - 1;//否则,R等于N-1。
                }
                //三元写法:R = (N - 1 - M) >= step ? (M + step) : (N - 1);
                //至此,左组和右组,都已经有序了;开始merge吧。
                merge(arr, L, M, R);
                //如果R来到数组arr的最后一个索引,直接break。直接去增加步长,即step*=2。
                if (R == N - 1) {
                    break;
                } else {//否则,L来到第二个整体的,左组的第一个索引位置。即R+1位置。
                    L = R + 1;
                }
            }
            //如果步长大于数组arr长度的一半,就break,不要将步长乘2了。因为会有溢出的风险。
            //不能写step >= N /2。不能加上等于。
            //因为当step刚好等于N的一半时,如果此时break,也就是没有将step乘2。也就没有继续将数组的右半部分,进行归并和排序。
            //导致整个数组arr,左组有序(N/2长度),右组也有序(N/2长度)。但是由于没有合并,所以整体并不是有序的。
            //故此处break的条件中,不能加等于。
            if (step > N / 2) {
                break;
            }
            step *= 2;//步长乘2

        }
    }

    //方式2的简化。非递归方式
    public static void mergeSort3(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        int mergeStep = 1;
        int N = arr.length;
        while (mergeStep < N) {
            //将L归零的这一句,一定要写在外层循环内,内层循环外。
            //因为当步长mergeStep翻倍以后,L需要归零。否则,mergeStep >= N - L的结果就是true了;导致直接break了,最后的结果也就是错误的了。
            int L = 0;
            //只要左组的第一个索引,小于数组arr的长度,就循环。
            while (L < N) {
                //L到N-1的个数,就是((N-1)-L)+1
                //如果L到N-1的个数,小于等于步长mergeStep,那么L+mergeStep就有溢出的风险。
                if (mergeStep >= N - L) {
                    break;
                }
                //M = L+步长再-1。
                int M = L + mergeStep - 1;
                 /*int R;
                //如果[M+1, N-1]上的数字个数(即N-1-M),大于步长mergeStep。则右组的最后一个为索引R,等于左右组整体的中点M,再加上mergeStep(M+mergeStep)。
                if (mergeStep < N - 1 - M) {
                    R = M + mergeStep;
                } else {//反之,如果[M+1, N-1]上的数字个数(即N-1-M),小等于步长mergeStep,也就是M(不含)后边的数,不够一个mergeStep时。则R等于最后一个索引,即N-1。
                    R = N - 1;
                }*/
                //三元表达式写法
                /*int R = (mergeStep >= N - 1 - M) ? N - 1 : M + mergeStep;*/
                int R = M + Math.min(mergeStep, N - 1 - M);

                merge(arr, L, M, R);
                L = R + 1;//将L重新定位
            }
            if (mergeStep > N / 2) {//解释同上一个"if (mergeStep > N / 2)"
                break;
            }
            mergeStep <<= 1;//mergeStep翻倍
        }
    }

    //生成随机数组
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        return arr;
    }

    //复制数组
    public static int[] copyArray(int[] arr) {
        if (arr == null) {
            return null;
        }
        int[] arr2 = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr2[i] = arr[i];
        }
        return arr2;
    }

    //判断两个数组是否相等
    public static boolean isEquals(int[] arr1, int[] arr2) {
        if ((arr1 != null && arr2 == null) || (arr2 != null && arr1 == null)) {
            return false;
        }
        if (arr1 == null && arr2 == null) {
            return true;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }

    //打印数组
    public static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int maxSize = 100;
        int maxValue = 1000;
        int testTimes = 100000;
        System.out.println("test start!");
        for (int i = 0; i < testTimes; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
//            int[] arr1 = {2, 3, 6, 4, 3, 1, 0};
//            printArr(arr1);
            int[] arr2 = copyArray(arr1);
            mergeSort1(arr1);
//            printArr(arr1);
//            Arrays.sort(arr2);
//            mergeSort2(arr2);
            mergeSort3(arr2);
//            printArr(arr2);
            if (!isEquals(arr1, arr2)) {
                System.out.println("出错了!");
                printArr(arr1);
                printArr(arr2);
                break;
            }
        }
        System.out.println("test end!");
    }

}

 

标签:归并,步长,int,arr,step,mergeStep,数组,排序
From: https://www.cnblogs.com/TheFloorIsNotTooHot/p/16614499.html

相关文章

  • Python的几种lambda排序方法
    1.对单个变量进行排序#lst=[[5,8],[5,3],[3,1]]lst.sort(key=lambdax:x[1])#lst=[[3,1],[5,8],[5,3]]以元素的第二个元素升序排列2.对多个变量进行排序......
  • LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
    34.在排序数组中查找元素的第一个和最后一个位置思路:与AcWing789一致classSolution{public:vector<int>searchRange(vector<int>&nums,inttarget){......
  • 复杂度分析、排序算法、二分法、堆的上调和下调
    1.认识复杂度与排序算法复杂度:认识时间复杂度就是看进行了多少次常数操作。(什么是常数操作?赋值、加减乘除运算等都是。调用API操作就不是如list.get(i)。)时间复杂度:在......
  • java算法:快速排序
    快速排序有没有既不浪费空间又可以快一点的排序算法呢?那就是“快速排序”啦!光听这个名字是不是就觉得很高端呢。假设我们现在对“61279345108”这个10个数进行......
  • Mysql Order By 字符串排序,1 10 2 20,以字符串排序,不是使用数字排序
    一、问题描述:Mysql排序时如果用的的字段为字符串型的,排序规则是这样的:如1,10,2,20,3,4,5,这种排序是按照字符从第一个字符开始比较出来的,但不是我想要的,我想要的是:1,2,3,4,5……,10,20......
  • 归并排序
    1.归并排序——分治#算法原理归并排序的思想就是分治,先递归分解数组,再合并数组。将数组分解到最小之后,再往上一层两两合并两个有序的数组,最终递归返回的就是一个排好......
  • AcWing算法基础课---第一讲基础算法---01排序
    快速排序步骤确定分界点:q[l],q[(l+r)/2],q[r],随机调整区间递归处理voidquick_sort(intq[],intl,intr){if(l>=r)return;//递归结束条件......
  • 分页和排序
    分页和排序排序--排序:升序asc,降序desc--orderby通过哪个字段排序,怎么排--查询的结果根据成绩降序排序SELECTs.`studentno`,`studentname`,`subjectname`,......
  • Mysql-排序与分页
    1.排序使用orderby进行排序:ASC(ascend)升序,DESC(descend)降序,一般把orderby语句放在select语句的结尾,多列排序的顺序按照orderby后的字段顺序进行......
  • 字典排序
    importoperatordefdeal_dict_sort():x=[{'name':'Homer','age':39},{'name':'Bart','age':10}]lx=sorted(x,key=operator.itemgetter('age'),......