排序算法——归并排序

欢迎大家参观我的新网站,悦来

前面我们了解了鸡尾酒排序桶排序,今天来学习归并排序,首先了解一下什么是归并排序:归并排序(合并排序)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(分而治之)的一个非常典型的应用将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,使再子序列段间有序看不明白,没关系,用图来直观展示:

这里写图片描述

首先准备一列待排序的数,将这一列待排序的数就行分割,分为左右两部分,我们这样考虑: 将左右两边的数进行排序,我们比较左右两边最小的数,就可以确定整列数中的最小数,一次这样进行比较次小数,就可以得到从小到大排列的全部数,这是典型的分治思想.将一个打的问题拆分为小的问题,我们这里要解决的是全部数的排序,分解为了左边排序,右边排序,再比较的分问题. 经过递归操作,就得到了上图中的步骤.
我们再来看一下动图:
是否明白了归并排序的原理了呢,就是分而治之,再归并统一.

下面我们看一下Java实现的归并排序代码:

     private static void sort(int[] array) {
        int[] resultArr = new int[array.length];
        merge(0, array.length-1, resultArr, array);
        System.out.println(Arrays.toString(resultArr));
    }
 
private static void merge(int left, int right, int[] resultArr, int[] array) {
        if (left >= right) {
            return;
        }
        // 中间分割点
        int middle = (left + right) / 2;
        // 左边起点与终点
        int leftStart = left;
        int leftEnd = middle;
 
        // 右边起点与终点
        int rightStart = middle + 1;
        int rightEnd = right;
        System.out.println(leftStart+"---"+leftEnd);
        // 左边再次分割
        merge(leftStart, leftEnd, resultArr, array);
        // 右边再次分割
        merge(rightStart, rightEnd, resultArr, array);
         
        // 进行比较归并
        int i = left;
        while (leftStart <= leftEnd && rightStart <= rightEnd) {
            resultArr[i++] = array[leftStart] < array[rightStart] ? array[leftStart++] : array[rightStart++];
        }
        while (leftStart <= leftEnd) {
            resultArr[i++] = array[leftStart++];
        }
        while (rightStart <= rightEnd) {
            resultArr[i++] = array[rightStart++];
        }
        for (i = left; i <= right; i++) {
            array[i] = resultArr[i];
        }
    }

大家可以看出来,是用递归实现的,有一些地方要稍微讲解一下
下面有三个while循环,第一个看判断条件,可以看出来是当左右两边有任意一边没有比较结束,就进行比较

第二个while循环是判断如果右边已经比较结束,则把左边数据依次赋值给结果数组

第三个while循环是判断左边比较已经结束,则把右边数据依次赋值给结果数组,经过三次循环比较,就可以将左右两边的数据比较完全.

还有一个for循环,看样子是把结果数组又赋值给了原数组,其实是因为,结果数组存放的是左右两边比较之后的结果,需要把比较之后的结果重新赋值给原数组,进行下一次的比较,不然的话,原数组永远不会变,比较也就没有了任何意义.

举个列子:

比如: 4 6 7 1

我们要首先比较4 和 6得到4 6,比较7 和 1得到1 7,这使得结果数组是4 6 1 7,原数组是4 6 7 1,下一次进行比较的时候,如果原数组不变,就变成了比较 4和7,6和1了,会得到错误结果4 7 1 6,而我们希望比较的是,4和1,6和7,所以需要把上一次的结果数组替换掉原数组,就得到了正确结果1 4 6 7.

我们不用递归用迭代来实现一下,代码如下:

private static void sort2(int[] array) {
        int[] result = new int[array.length];
        // 分割之后左右两边的数据长度
        for (int i = 1; i < array.length; i *= 2) {
            // 遍历左右两边的数据块
            for (int j = 0; j < array.length; j += i * 2) {
                int middle = (j + i) < array.length ? j + i : array.length;
                int left = j;    // 记录最左边的下标
                int leftBegin = j;//左边数据快左始下标
                int leftEnd = middle;//左边数据快结束下标
                int rightBegin = middle;//右边数据快起始下标
                //右边数据快结束下标
                int rightEnd = (j + 2 * i) < array.length ? (j + 2 * i) : array.length;
                while (leftBegin < leftEnd && rightBegin < rightEnd) {
                    result[left++] = array[leftBegin] < array[rightBegin] ? array[leftBegin++] : array[rightBegin++];
                }
                while (leftBegin < leftEnd) {
                    result[left++] = array[leftBegin++];
                }
                while (rightBegin < rightEnd) {
                    result[left++] = array[rightBegin++];
                }
            }
            int[] temp = array;
            array = result;
            result = temp;
        }
        result = array;
        System.out.println(Arrays.toString(result));
    }

迭代版与递归版的一点不同是,递归进行了数据分割,先排左边,再排右边,最后归并,迭代使用了数据块长度来进行左右分割的,并且是左右排序同时进行,所以迭代中的for循环更新原数组,在这里就变成了数组之间的直接赋值,因为上一步的结果数组是所有的左右数据块排序之后的.

在Java的Arrays.sort()排序函数中,进行了优化,不同的数组,不同的长度选用的排序算法也不一样,如果小于47则使用插入排序,如果数组长度小于286的时候而大于47,优先使用快速排序,而且还有判断数组是否已经接近排好顺序,数组是否高度结构化等等条件,在真实项目中直接用就可以,一般来说比你自己写的要高效得多,但我们还是要知道这些算法的具体实现.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MPI(Message Passing Interface)是一种用于并行计算的编程模型和库。归并排序是一种经典的排序算法,适合并行计算。 在MPI中,可以通过发送和接收消息来实现进程间的通信。下面是一个基于MPI的归并排序的伪代码: ```python def parallel_merge_sort(data): # 获取进程总数和当前进程编号 size = MPI.COMM_WORLD.Get_size() rank = MPI.COMM_WORLD.Get_rank() # 计算每个进程要处理的数据量 chunk_size = len(data) // size remainder = len(data) % size # 将数据分发到各个进程 if rank == 0: for i in range(size): if i < remainder: chunk = data[i * (chunk_size + 1):(i + 1) * (chunk_size + 1)] else: chunk = data[remainder + i * chunk_size:remainder + (i + 1) * chunk_size] MPI.COMM_WORLD.send(chunk, dest=i, tag=0) # 接收数据 chunk = MPI.COMM_WORLD.recv(source=0, tag=0) # 对本地数据进行排序 chunk.sort() # 归并排序 for step in range(size): # 计算要交换数据的进程编号 partner = (rank + step) % size # 发送和接收数据 sendbuf = chunk recvbuf = MPI.COMM_WORLD.recv(source=partner, tag=step) if rank < partner: sendtag = step recvtag = step + size else: sendtag = step + size recvtag = step MPI.COMM_WORLD.send(sendbuf, dest=partner, tag=sendtag) chunk = merge(chunk, recvbuf) # 将排序好的数据返回 if rank == 0: result = [] for i in range(size): chunk = MPI.COMM_WORLD.recv(source=i, tag=size) result.extend(chunk) return result else: MPI.COMM_WORLD.send(chunk, dest=0, tag=size) ``` 在这个算法中,首先将原始数据分发到各个进程,然后每个进程对本地数据进行排序,接着对每个步骤进行归并排序,并且使用MPI的send和recv函数进行交换数据。最后将排序好的数据返回到主进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值