归并排序

归并排序

分解:分解待排序的n个元素为各具n/2个元素的子序列
解决:使用归并排序递归地排序两个子序列
合并:合并两个已排序的子序列产生已排序的答案

介绍

复杂度:T(n) = cnlgn + cn

关键点:归并排序算法的关键是"合并"步骤中两个已排序序列的合并。我们调用MERGE(A, p, q, r)来完成合并,其中A是一个数组,p、q和r是数组下标,满足p<=q<r。该过程假设子数组A[p…q]和[q + 1…r]都已排序。合并这两个子数组形成单一的已排序好子数组并代替当前的子数组A[p…r]

合并排好序的数组伪代码

    MERGE(A, p, q, r)
    1  n1 = q - p + 1
    2  n2 = r - q
    3  let L[1..n + 1] and R[1..n2 + 1] be new arras
    4  for i = 1 to n1
    5     L[i] = A[p + i -1]
    6  for j = 1 to n2
    7     R[j] = A[q + j]
    //设置哨兵
    8  L[n1 + 1] = ∞
    9  R[n2 + 1] = ∞
    10 i = 1
    11 j = 1
    12 for k = p to r
    13    if L[i] <= R[j]
    14       A[k] = L[i]
    15       i = i + 1
    16    else A[k] = R[j]
    17         j = j + 1

推导过程:

  • 初始化:循环不变式的第一次迭代前,k = p,子数组A[p…k-1]为空,空的子数组包含L、R的的r-p = 0个最小元素。
    因为i = j = 1,所以L[i]、R[j]为各自子数组的第一个最小的元素

  • 保持:假设L[i] <= R[j],则L[i]是未被复制到A中的最小元素。因为A[p, k-1]包含 k - q个元素,代码第14行将L[i]复制回A[k]后,A数组将包含k - q + 1个元素。增加i、k的值后为下次迭代重新建立了循环不变式。反之,若L[i] > R[i],则16~17行执行适当的操作维持循环不变式

  • 终止:终止时k = r + 1,根据循环不变式,子数组A[p…k-1]就是A[p…r]且按从小到大的顺序包含L[1…n1 + 1]和
    R[1…n2 + 1]中的k-p = r - p + 1个最小元素。数组L和R一起包含n1 + n2 + 2 = r - p + 3个元素。初两个最大的哨兵
    元素外其余元素都被复制回数组A

归并排序伪代码

    MERGE-SORT(A, p, r)
    1   if p < r
    2       q = (p + r)/2  //向下取整
    3       MERGE-SORT(A, p, q)
    4       MERGE-SORT(A, q + 1, r)
    5       MERGE(A, p, q, r)

归并排序的过程就是不断递归的将数组进行二分拆分后合并排序的过程

Java实现

实现代码

    package com.aim.algorithm.sort.impl;

    import com.aim.algorithm.sort.Sort;
    import org.springframework.stereotype.Service;

    @Service
    public class MergeSort implements Sort {
        @Override
        public int[] sort(int[] input) {
            mergeSort(input, 0, input.length-1);
            return input;
        }

        public void mergeSort(int[] input, int start, int end){
            if(start < end){
                int mid = (int) Math.floor(((double) start + end)/2);
                mergeSort(input, start, mid);
                mergeSort(input, mid + 1, end);
                merge(input, start, mid, end);
            }
        }

        private void merge(int[] input, int start, int mid, int end){
            int l = mid - start + 1;
            int r = end - mid;
            int[] left = new int[l + 1];
            int[] right = new int[r + 1];
            for(int i = 0; i < l; i++){
                left[i] = input[start + i];
            }
            for(int j = 0; j < r; j++){
                right[j] = input[mid + j + 1];
            }
            left[l] = Integer.MAX_VALUE;
            right[r] = Integer.MAX_VALUE;
            int i = 0;
            int j = 0;
            for(int k = start; k <= end; k++){
                if(left[i] <= right[j]){
                    input[k] = left[i];
                    i++;
                }else {
                    input[k] = right[j];
                    j++;
                }
            }
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值