【算法导论 in lambda】并归排序

并归排序的过程就是一个先拆再合并的过程,先拆到全是不能再拆的最小数组,然后组与组之间合并,排序的过程在合并的过程中执行。

所以整个算法分两部分,split和merge

先说merge吧,将两个数组合并为新数组,符合从大到小。

 public int[] merge(int[] c1, int[] c2) {
        int[] ret = new int[c1.length + c2.length];
        for (int i = 0, j = 0, index = 0; i < c1.length || j < c2.length; ) {
            if (i < c1.length) {
                if (j < c2.length) {
                    if (c1[i] >= c2[j]) {
                        ret[index++] = c2[j++];
                    } else {
                        ret[index++] = c1[i++];
                    }
                } else {
                    ret[index++] = c1[i++];
                }
            } else {
                ret[index++] = c2[j++];
            }
        }
        return ret;
    }

  邓老师的教案给出过另外一种复杂校验的版本,不过其教案上也注明了,从效率的角度而言,还是这样拆开来写比较好。。。

  merge方法的条件是两个已经排好序的数组c1和c2,返回值则是也已经排好序的包括c1和c2所有内容的另一数组。

  

  其中是有一个for循环来着,逻辑上可以用IntStream.iterate来代替,但代码中对c1,c2,ret的操作都是寻秩访问,IntStream.iterate的执行过程大多是对指针的操作而非数据本身,就很麻烦。。不知道咋写。

  split方法需要执行的操作是将数组分到不能再分。

  不过这边不需要返回执行结果,只需要对于每个拆分出来的两个对象继续进行split操作,当不能进行split了,则merge。

  spilit的实现如下:

 public void split(int[] ins, int a, int b) {//3  6
        int mid = (a + b) / 2;
        if (b - a <= 2) {
            //无非两种情况
            if (b - 1 > a && ins[a] > ins[b - 1]) {
                int temp = ins[a];
                ins[a] = ins[b - 1];
                ins[b - 1] = temp;
            }
            return;
        }
        split(ins, a, mid);
        split(ins, mid, b);
        merge(ins, a, mid, mid, b);
    }

  split传入的参数就是待排序数组ins,排序区间[a,b)本身是个递归算法,所有操作直接在ins上执行就好了,所以是不需要返回值的(返回值也没啥意义)。

  其逻辑就是拆分拆分拆分和mergemergemerge。不过需要个if去判断一下待排序区间的大小,如果区间只有两个元素,判断一下然后进行一下交换就行了。至于为毛要有这个一个if,因为if语句后面的语句依然执行的是split,其中依然需要传入排序区间[a,b),上文也提到了那这里的这个if,指的,也是处理的,就是这个过程,因为merge是在split之后访问的,所以这个判断过程只能写到split里面,而不能搞到merge里面。

  之后需要对merge方法进行一些改造,大致就是,原来的merge是由两个int数组创建一个新的int数组,而新的merge方法,最好是接近原地算法,在原数组上直接进行修改,那用数组,跟排序区间,即可表示原来的一个数组。

public void merge(int[] ori, int c1_left, int c1_right, int c2_left, int c2_right) {
        int[] temp = new int[c1_right - c1_left];
        System.arraycopy(ori, c1_left, temp, 0, temp.length);

        int c1_length = c1_right - c1_left;

        for (int i = 0, j = c2_left, index = c1_left; i < c1_length || j < c2_right; ) {
            if (i < c1_length) {
                if (j < c2_right) {
                    //都合法
                    if (temp[i] >= ori[j]) {
                        ori[index++] = ori[j++];
                    } else {
                        ori[index++] = temp[i++];
                    }
                } else {
                    ori[index++] = temp[i++];
                }
            } else {
                ori[index++] = ori[j++];
            }
        }
    }

  不过似乎无论是在split还是在merge当中,由于其大量的寻秩操作,其对秩和位置进行操作,而不是对数组中的值进行操作,流以及函数式编程,在这种算法中的应用范围不广,并不适合。

原文地址:https://www.cnblogs.com/callmegaga/p/9869251.html

时间: 2024-08-11 17:47:29

【算法导论 in lambda】并归排序的相关文章

【算法导论学习-014】计数排序(CountingSortTest)

参考:<算法导论>P194页 8.2节 Counting sort 1.Counting sort的条件 待排序数全部分布在0~k之间,且k是已知数:或者分布在min~max之间,等价于分布在0~max-min之间,max和min是已知数. 2.java 实现 /** * 创建时间:2014年8月17日 下午3:22:14 项目名称:Test * * @author Cao Yanfeng * @since JDK 1.6.0_21 类说明: 计数排序法,复杂度O(n), 条件:所有数分布在0

算法导论 第7章 高速排序

高速排序在最坏情况下的时间复杂度为O(n^2),尽管在最坏情况下执行时间比較差,可是高速排序一般是用于排序的最佳选择.由于其平均性能相当好,期望的执行时间为O(nlgn),且在O(nlgn)的记号中隐含的常数因子非常小. 高速排序和合并排序有相似之处,都是须要划分序列,在合并排序中.划分的过程非常easy.直接选择元素序列的中间位划分位置,排序是在合并的过程中实现的,所以合并排序的合并过程非常重要.相比合并排序,高速排序就没有合并的过程.仅仅有划分,高速排序的划分过程非常重要,排序是在划分的过程

算法导论——lec 08 线性时间排序

之前我们介绍了几种O(nlgn)的排序算法:快速排序.合并排序和堆排序,本节我们介绍基于比较的排序算法的下界以及几个线性时间的排序算法--计数排序.基数排序.桶排序. 一. 比较排序算法的下界 1. 决策树模型:比较排序可以被抽象的视为决策树.一棵决策树是一棵满二叉树,表示某排序算法 作用于给定输入所做的所有比较. 排序算法的执行对应于遍历一条从树的根到叶结点的路径.在每个内节结点处要做比较. 要使排序算法能正确的工作,其必要条件是,n 个元素的n!种排列中的每一种都要作为决策树 的一个叶子而出

算法导论学习之插入排序+合并排序

最近准备花时间把算法导论详细的看一遍,强化一下算法和数据结构的基础,将一些总结性的东西写到博客上去. 一.插入排序 算法思想:如果一个数组A,从A[1–n-1]都是有序的,然后我们将A[n]插入到A[1–n-1]的某个合适的位置上去那么就可以保证A[1–n]都是有序的.这就是插入排序的思想:具体实现的时候我们将数组的第一个元素看出有序,然后从第二个元素开始按照上面的步骤进行插入操作,直到插入最后一个元素,然后整个数组都是有序的了. 时间复杂度分析:代码中有两重for循环,很容易看出时间复杂度是n

《算法导论》 — Chapter 7 高速排序

序 高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组.最坏情况执行时间为O(n^2). 尽管这个最坏情况执行时间比較差.可是高速排序一般是用于排序的最佳有用选择.这是由于其平均性能相当好.期望的执行时间为O(nlgn).且O(nlgn)中隐含的常数因子非常小.另外它还能够进行就地排序在虚拟环境中也能非常好的工作. GitHub chapter 7 程序代码下载 原理 高速排序也和合并排序一样,基于分治法,分为分解.解决.合并三个步骤. 分解:数组array[low-hig

《算法导论》读书笔记之排序算法—Merge Sort 归并排序算法

自从打ACM以来也算是用归并排序了好久,现在就写一篇博客来介绍一下这个算法吧 :) 图片来自维基百科,显示了完整的归并排序过程.例如数组{38, 27, 43, 3, 9, 82, 10}. 在算法导论讲分治算法一章的时候提到了归并排序.首先,归并排序是一个分治算法. 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表, 即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列. merg() 函数是用来合并两个已有序的数组.  是整个算法的关键. 那么归并

算法导论之六:线性时间排序之 决策树&amp;计数排序

本系列前五篇都是讲述的比较排序算法,从本文开始,将进入线性时间排序.什么是比较排序,简单的说,就是排序的过程依赖于数组中数据大小的比较,从而来确定数据在排好序输出时的位置. 比较排序法比较直观,但是也有它的不足,我们容易证明任何比较排序法,在最坏的情况下的时间复杂度的下限都是 nlgn.要证明这个问题,我们首先要搞清楚一个模型:决策树模型. 一.决策树模型 什么是决策树?决策树从形态上来讲,是一颗完全二叉树,它除叶子节点之外,其他层的节点都是满的.它的每一个叶子节点表示对输入数据组合的一种排序可

算法导论第八章线性时间排序

一.线性时间排序算法历史概览 计数排序首先是由 Harold H. Seward 于1954年提出,而且他还提出将计数排序和基数排序进行结合的思想:基数排序是L.J.Comrie于1929年首次在一篇描述卡片穿孔机文档中提出的一种方法,它是从最低有效位开始,对一个有多位数组成的数进行排序的方法:而桶排序的基本思想则由E.J.Isaac和R.C.Singleton于1956年提出的,之后很多研究人员在这三种算法的基础上针对不同的应用场景又进一步改进,到了今天一个很成熟.很通用的地步. 二.O(nl

算法导论学习之线性时间排序+排序算法稳定性终结

前面我们学习的几种排序算法都是基于比较的,对于任何输入数据他们都是适用的,其最坏的时间复杂度不会低于nlgn: 但对于一些比较特殊的输入数据,我们可以不采取比较的方法而是采用其它的方法对其进行排序,以达到线性的时间复杂度.下面就来介绍三种这样的算法:计数排序,基数排序,桶排序(因为这几种算法不常见,我只实现了计数排序,其它两种排序用伪代码表示). 一.计数排序 算法思想:给定n个位于0–k之间的数(k是一个不太大的整数),我们可以统计出每个数前面有多少个小于它的数,然后就可以直接确定这个数在数组