排序算法——归并排序

好长时间没有更新了,有没有想我呢?我们接着说排序算法吧

今天为大家梳理的是归并排序

合并排序(Merge Sort)算法就是将多个有序数据表合并成一个有序数据表。如果参与合并的只有两个有序表,则称为二路合并。对于一个原始的待排序序列,往往可以通过分割的方法来归结为多路合并排序。下面以二路合并为例,来介绍合并排序算法。

合并排序算法
一个待排序的原始数据序列进行合并排序的基本思路是,首先将含有n个结点的待排序数称列看作由n个长度为1的有序子表组成,将其依次两两合并,得到长度为2的若干有序子表:然后,再对这些子表进行两两合并,得到长度为4的若干有序子表.…,重复上述过程,一直到最后的子表长度为n,从而完成排序过程。

1.合并排序算法原理
下面通过一个实际的例子来分析合并排序算法的执行过程,以加深读者的理解。

假设有9个需要排序的数据序列 67、65、77、38、97、3、33、49、34。合并排序算法的操作步骤如下:
(1)首先将9个原始数据看成9个长度为1的有序子表。每一个子表中只有一个数据,这里可以认为单个数据是有序的。
(2)将这9个有序子表两两合并,为了方便,将相邻的子表进行两两合并。例如,将第1、2个合并在一起,第3、4个合在一起···,最后一个没有合并的,就单独放在那里,直接进入下一遍合并,如图所示。

原始数据:
在这里插入图片描述

(3)经过第1遍的合并,得到长度为2的有序表序到,再将这些长度为2的有序却学。
两合并,如图所示。
第1遍合并结果:

在这里插入图片描述

(4)重复两两合并,经过第2遍合并,得到长度为4的有序表序列,再将这些长度为4的有序表进行两两合并,如图所示。
第2遍合并结果:
在这里插入图片描述

(5)经过第3遍合并,得到长度为8的有序表序列,以及最后只有一个元素的序列,如图所示:
第3遍合并结果:
在这里插入图片描述
(6)将这两个序列进行合并,即可完成合并排序,最后的结果如图所示:
第四遍合并结果:

在这里插入图片描述
2.合并排序算法

代码如下:

/**
     *
     * @param arr  排序的数组
     * @param left 左边有序序列的开始索引
     * @param right 右边索引
     * @param mid   中间索引
     * @param temp  中转数组
     */
    public static void merge(int[] arr,int left,int right,int mid,int[] temp){
        //System.out.println("×××××");//合并的次数
        int i = left;//初始化i,左边有序序列的初始索引
        int j=mid+1;//初始化j,右边有序序列的初始索引
        int t=0;//指向temp数组的当前索引

        //1.先把左右两边(有序)的数据按照规则填充到temp数组
        //直到左右两边的有序序列有一方处理完毕为止
        while(i<=mid && j<=right){//继续
            //如果左边的有序序列当前元素小于等于右边有序序列的当前元素
            //就将左边的元素填充到temp数组中
            //然后t后移,i后移
            if(arr[i]<=arr[j]){
                temp[t]=arr[i];
                t+=1;
                i+=1;
            }else{//反之右边的小于等于左边的,将右边的填充到temp中
                temp[t]=arr[j];
                t+=1;
                j+=1;
            }
        }

        //2.把有剩余数据的一边的数据依次全部填充到temp
        while(i<=mid){//说明左边的有序序列还有剩余的,就全部填充到temp
            temp[t]=arr[i];
            t+=1;
            i+=1;
        }
        while(j<=right){//说明右边的有序序列还有剩余的,就全部填充到temp
            temp[t]=arr[j];
            t+=1;
            j+=1;
        }

        //3.将temp数组的元素copy到arr
        //注意并不是每次都拷贝所有
        t=0;
        int tempLeft=left;//
        //第一次合并时tempLeft=0,right=1  //tempLeft=2,right=3
        // tempLeft=0,right=3  //tempLeft=0,right=7
        //System.out.println("tempLeft="+tempLeft+"\t"+"right="+right);
        while(tempLeft<=right){
            arr[tempLeft]=temp[t];
            t+=1;
            tempLeft+=1;
        }

    }

运行结果如图所示:
在这里插入图片描述

我们还可以去测试它的运行效率,这里给出代码,大家自行检测:

package DataStructures.pro0925Sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * @Author DreamYee
 * @Create 2019/11/16 17:15
 */
public class MergeSort {
    public static void main(String[] args) {
        int[] arr={8,4,5,7,1,3,6,2};
        int[] temp=new int[arr.length];
        System.out.println("归并排序前:"+ Arrays.toString(arr));
        mergeSort(arr,0,arr.length-1,temp);
        System.out.println("归并排序后:"+ Arrays.toString(arr));

        /*
        //创建要给80000个数据的数组
        int[] arr=new int[800000];
        int[] temp=new int[arr.length];//归并排序需要额外的空间
        for(int i=0;i<800000;i++){
            arr[i]=(int)(Math.random()*8000000);//生成一个[0,8000000)数
        }

        Date date1=new Date();
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str=simpleDateFormat.format(date1);
        System.out.println("排序前的时间:"+date1Str);

        mergeSort(arr,0,arr.length-1,temp);

        Date date2=new Date();
        String date2Str=simpleDateFormat.format(date2);
        System.out.println("排序后的时间:"+date2Str);

        //System.out.println("归并排序后:"+ Arrays.toString(arr));
        */
    }

    //分+合的方法
    public static void mergeSort(int[] arr,int left,int right,int[] temp){
        if(left<right){
            int mid=(left+right)/2;  //中间索引
            //向左递归进行分解
            mergeSort(arr,left,mid,temp);
            //向右递归进行分解
            mergeSort(arr,mid+1,right,temp);
            //每分解一次就合并一次
            merge(arr,left,right,mid,temp);

        }
    }

    //合并的方法

    /**
     *
     * @param arr  排序的数组
     * @param left 左边有序序列的开始索引
     * @param right 右边索引
     * @param mid   中间索引
     * @param temp  中转数组
     */
    public static void merge(int[] arr,int left,int right,int mid,int[] temp){
        //System.out.println("×××××");//合并的次数
        int i = left;//初始化i,左边有序序列的初始索引
        int j=mid+1;//初始化j,右边有序序列的初始索引
        int t=0;//指向temp数组的当前索引

        //1.先把左右两边(有序)的数据按照规则填充到temp数组
        //直到左右两边的有序序列有一方处理完毕为止
        while(i<=mid && j<=right){//继续
            //如果左边的有序序列当前元素小于等于右边有序序列的当前元素
            //就将左边的元素填充到temp数组中
            //然后t后移,i后移
            if(arr[i]<=arr[j]){
                temp[t]=arr[i];
                t+=1;
                i+=1;
            }else{//反之右边的小于等于左边的,将右边的填充到temp中
                temp[t]=arr[j];
                t+=1;
                j+=1;
            }
        }

        //2.把有剩余数据的一边的数据依次全部填充到temp
        while(i<=mid){//说明左边的有序序列还有剩余的,就全部填充到temp
            temp[t]=arr[i];
            t+=1;
            i+=1;
        }
        while(j<=right){//说明右边的有序序列还有剩余的,就全部填充到temp
            temp[t]=arr[j];
            t+=1;
            j+=1;
        }

        //3.将temp数组的元素copy到arr
        //注意并不是每次都拷贝所有
        t=0;
        int tempLeft=left;//
        //第一次合并时tempLeft=0,right=1  //tempLeft=2,right=3
        // tempLeft=0,right=3  //tempLeft=0,right=7
        //System.out.println("tempLeft="+tempLeft+"\t"+"right="+right);
        while(tempLeft<=right){
            arr[tempLeft]=temp[t];
            t+=1;
            tempLeft+=1;
        }

    }
}

好啦,归并排序就说到这,大家有什么可以留言哦

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、付费专栏及课程。

余额充值