排序算法 - 归并排序图文解析

本文深入讲解归并排序算法,一种基于分治法的有效排序方法。文章详细阐述了归并排序的具体步骤,包括如何通过递归将序列分割为子序列,以及如何合并这些子序列使其有序。同时,提供了Java实现代码,并进行了归并排序的时间和空间复杂度分析。

归并排序

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

归并排序(分治法):

  • 分:将元素序列不断细分,分到最小子序列段(递归处理)
  • 治:合并、排序,递归分子序列段,回溯返回时合并排序
    在这里插入图片描述

递归的思想解决:分方法一共执行了7次
在这里插入图片描述


归并排序具体步骤

排序[8, 4, 5, 7, 1, 3, 6, 2]数组:

  1. 递归分割数组,逻辑上形成8个子序列段(通过left、right指针分割,当left、right都指向同一个数组元素,逻辑上这就是一个独立的子序列段)
    在这里插入图片描述

  2. 准备一个和原数组一样容量的初始数组temp,用于存放临时数据,有一个mid指针int mid = (left + right) / 2;指向子序列段的中间
    在这里插入图片描述

  3. 治:回溯返回到原来的方法,开始合并排序,根据判断left < right,第一轮是两个元素比较:[0,1],[2,3],[4,5],[6,7],比较值时,该子序列分为两边:[left,mid],[mid+1,right],即[0]-[1],比较arr[left]与arr[mid+1]的值,较小的放入temp[0],另一边放入temp[1],然后temp数组值拷贝到arr
    在这里插入图片描述
    在这里插入图片描述

  4. 后续都是这样运行,直到最后排序[4,5,7,8] [1, 2, 3, 6]
    在这里插入图片描述

在这里插入图片描述


Java实现归并排序

根据上面的步骤,使用递归+分治实现归并排序

package com.company.sort;

import java.util.Arrays;

/**
 * 归并排序算法
 */
public class MergetSort {

    private static int num = 0;

    public static void main(String[] args) {
        int[] arr = {8,4,5,7,1,3,6,2};
        int length = arr.length;
        int[] temp = new int[length];

        mergetSort(arr,0,length - 1,temp);

        System.out.println("归并后的数组:"+ Arrays.toString(arr));
        System.out.println("归并排序merget执行了:"+num + "次");
    }

    /**
     * @param arr 要排序的原始数组
     * @param left 左边有序序列的初始索引
     * @param right 右边有序序列的最右边索引
     * @param temp 中转的数组
     * 分+合
     */
    public static void mergetSort(int[] arr,int left,int right,int[] temp){

        if (left < right){
            int mid = (left + right) / 2;
            //向左递归进行分解
            mergetSort(arr,left,mid,temp);
            //向右递归进行分解
            mergetSort(arr,mid+1,right,temp);

            merget(arr,left,mid,right,temp);
        }
    }

    /**
     * @param arr 要排序的原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边有序序列的最右边索引
     * @param temp 中转的数组
     * 合并方法
     */
    public static void merget(int[] arr,int left,int mid,int right,int[] temp){
        num++;
        //左边序列的初始索引
        int i = left;
        //右边有序序列的初始索引
        int j = mid + 1;
        //中转数组的索引
        int t = 0;

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

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


        //3. 将temp数组的元素拷贝到原数组arr
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right){
            arr[tempLeft] = temp[t];
            t++;
            tempLeft++;
        }
    }

}

在这里插入图片描述


归并排序速度测试

归并排序时间复杂度:O(nlog2n)
空间复杂度:O(N),需要一个与原数组相同长度的数组temp辅助排序

时间复杂度是一回事,速度又是一回事,我们可以设置80000个0~80000内的数据排序

    public static void main(String[] args) {
        //int[] arr = {8,4,5,7,1,3,6,2};

        int[] arr = new int[80000];
        for(int i = 0 ;i <arr.length;i++){
            //随机生成80000内的整数
            arr[i] = (int) (Math.random()*80000);
        }
        int length = arr.length;
        int[] temp = new int[length];

        Date dataBefore = new Date();

        mergetSort(arr,0,length - 1,temp);

        Date dateAfter = new Date();

        System.out.println("消耗了:"+(dateAfter.getTime()-dataBefore.getTime())+"ms");
    }

在这里插入图片描述

归并排序确实是非常快
同为平均时间复杂度O(nlog2n)

希尔排序也差不多:
在这里插入图片描述

快速排序在小数据量上较差一些,但如果在千万级那种数据量大的情况下更强:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值