归并排序

归并排序,是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。

1.主要思路:归并排序是分治思想,分治模式在每一层递归上有三个步骤:

  • 分解:将n个元素分成个含n/2个元素的子序列。
  • 解决:用合并排序法对两个子序递归的排序。
  • 合并:合并两个已排序的子序列已得到排序结果。

2.实现逻辑

2.1 迭代法

① 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
② 设定两个指针,最初位置分别为两个已经排序序列的起始位置
③ 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
④ 重复步骤③直到某一指针到达序列尾
⑤ 将另一序列剩下的所有元素直接复制到合并序列尾

2.2 递归法

① 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
② 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
③ 重复步骤②,直到所有元素排序完毕

3. 动图演示

根据master 公式

   master公式(也称主方法)是用来利用分治策略来解决问题经常使用的时间复杂度的分析方法,(补充:分治策略的递归解法还有两个常用的方法叫做代入法和递归树法,以后有机会和亲们再唠),众所周知,分治策略中使用递归来求解问题分为三步走,分别为分解、解决和合并,所以主方法的表现形式:

T [n] = aT[n/b] + f (n)(直接记为T [n] = aT[n/b] + T (N^d))

其中 a >= 1 and b > 1 是常量,其表示的意义是n表示问题的规模,a表示递归的次数也就是生成的子问题数,b表示每次递归是原来的1/b之一个规模,f(n)表示分解和合并所要花费的时间之和。

解法:
①当d<logb a时,时间复杂度为O(n^(logb a))
②当d=logb a时,时间复杂度为O((n^d)*logn)
③当d>logb a时,时间复杂度为O(n^d)

为 2T(N/2)规模

 为 O(1)复杂度

  为O(N)

所以 T(N) = 2T (N/2) +O (N)

a=2 b=2 d=1  满足 d=logb a

所以 时间复杂度为O((n^d)*logn)= O(N*logn)

复杂度分析

平均时间复杂度:O(nlogn)
最佳时间复杂度:O(n)
最差时间复杂度:O(nlogn)
空间复杂度:O(n)
排序方式:In-place
稳定性:稳定

代码实现:

public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {1,4,7,8,3,6,9};
        sort(arr,0,arr.length-1);
        print(arr);
    }

    private static void sort(int[] arr, int left, int right) {
        if (left == right) return;
        // 分两半
        // 也可以写成   int mid = left + ((right - left) >> 1);
        // 注意 不能写成 int mid = left + (right - left) >> 1; 因为 >> 运算符的优先级 低于 +
        int mid = left + (right - left)/2;
        // 左排序
        sort(arr,left,mid);
        // 右排序
        sort(arr,mid+1,right);

        merge(arr,left,mid+1,right);
    }

    private static void merge(int[] arr,int leftPtr,int rightPtr,int rightBound) {
        int mid= rightPtr - 1;
        int[] temp = new int[rightBound - leftPtr + 1];

        int i=leftPtr;
        int j = rightPtr;
        int k=0;

         while (i <= mid && j <= rightBound){
             // i++为先参与运算,再赋值,例如arr[i++],先计算arr[i],再赋值i=i+1;
             temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
         }
         while (i <= mid) temp[k++]=arr[i++];
         while (j <= rightBound) temp[k++]=arr[j++];
         // 将 temp 数组放入到 arr 数组中
        for (int m = 0; m < temp.length; m++) arr[leftPtr + m] = temp[m];
    }
    static void swap(int[] arr,int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }
    static void print(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dayaoK

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值