[剑指Offer学习] :归并排序原理及Java实现

本文深入解析归并排序的原理与实现,包括递归与非递归两种方式,详细阐述算法的分治策略,以及时间与空间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

剑指Offer:归并排序原理及Java实现

算法描述

原理
归并排序:归并排序(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。

  • 基本步骤
    分而治之(divide - conquer):每个递归过程涉及三个步骤
    ① 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素;
    ② 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作;
    ③ 合并: 合并两个排好序的子序列,生成排序结果。
    性能分析
    归并排序是一种稳定的排序算法。
  • 空间复杂度:O(n),归并排序的主要问题在于它需要一个与待排序数组一样大的辅助数组空间,所以其空间复杂度为O(n)。
  • 时间复杂度:O(nlogn),由于归并排序每次划分时两个子序列的长度基本一样,所以归并排序最好、最差和平均时间复杂度都是O(nlogn)。
    实现方法
    归并排序的实现分为递归实现与非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。
    归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:
    1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
    2、设定两个指针,最初位置分别为两个已经排序序列的起始位置
    3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
    4、重复步骤3直到某一指针到达序列尾
    5、将另一序列剩下的所有元素直接复制到合并序列尾
    实现代码如下所示

代码展示

public class N3归并排序 {
    public static void merge(int[] a,int left,int mid,int right){
        //辅助数组空间
        int[] tmp = new int[a.length];
        //p1、p2是检测指针,分别指向两个子序列的左边
        int p1 = left,p2 = mid+1,k = left;

        //比较两个子序列
        while(p1 <= mid && p2 <= right){
            if(a[p1] < a[p2])
                tmp[k++] = a[p1++];
            else
                tmp[k++] = a[p2++];
        }

        //如果其中一个子序列没有检测完,直接将后面的所有元素加到tmp上
        while(p1<=mid)
            tmp[k++] = a[p1++];    //把左边剩余的数移入数组
        while (p2<=right)
            tmp[k++] = a[p2++];    //把右边剩余的数移入数组

        //将tmp数组复制回原数组
        for(int i = left ; i <=right ; i++)
            a[i] = tmp[i];
    }

    // 01递归实现的归并排序(自顶向下)
    public static int[] mergeSort(int[] a,int low,int high){
        if(low<high){
            int mid = (low + high)/2; //划分子序列
            mergeSort(a,low,mid);
            mergeSort(a,mid+1,high);
            merge(a,low,mid,high);     //合并子序列
        }
        return a;
    }

    //02非递归(迭代)实现的归并排序(自底向上)
    public static int[] mergeSortIteration(int[] a,int len){
        //子数组索引,前一个为a[left...mid],后一个为a[mid+1...right]
        int left,mid,right;
        //子数组的大小i初始为1,每轮翻倍
        for(int i = 1; i < len; i *=2){
            left = 0;
            //后一个子数组存在,需要归并
            while(left + i < len){
                mid = left + i -1;
                //后一个子数组可能不够
                right = mid + i < len ? mid + i : len -1;
                merge(a,left,mid,right);
                left = right +1; //前一个子数组索引向后移动
            }
        }

        return a;
    }

    public static void main(String[] args) {
        int[] a = {49,38,65,97,76,13,27,50};
        mergeSort(a,0,a.length-1);
        System.out.println("递归实现,排好序的数组:");
        for(int i : a)
            System.out.println(i+" ");

        mergeSortIteration(a,a.length-1);
        System.out.println("非递归实现,排好序的数组:");
        for(int i : a)
            System.out.println(i+" ");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值