十大排序算法之归并排序

归并排序

归并排序是包含归并思想的排序方法,它是分治法(Divide and Conquer)的一个典型应用。所谓分治,即将问题“分”(Divide)为更小的问题进行递归求解,再将得到的各个递归结果合并在一起,达到“治”(Conquer)问题的目的,也称“分而治之”。
“分”的阶段可一分为二、一分为三⋯⋯,据此我们也将归并排序分为二路归并、三路归并⋯,此处以二路归并为例进行讲解。

1. 算法思想

先将原数组均分为子序列,一生二,二生四,四生无穷,然后使每个子序列有序,再将两个有序子序列合并为一个有序序列,直到无穷合四,四合二,二合一。

2. 算法步骤

(1)将待排序数组一分为二,再将两个子序列一分为二,成为两个新的待排序数组。
(2)重复步骤(1),直到待排序数组的长度为1。
(3)按原路径将长度为1的两个数组合成一个有序序列,然后一直向前合并,最终就会得到一个完整的有序序列。
归并排序算法的排序步骤如下图所示。
在这里插入图片描述

注意:“分”阶段的结构和完全二又树一模一样,这意味着我们可以使用递归和选代,递归深度为 l o g 2 n log_2n log2n

3. 算法分析

归并排序是一种十分高效的算法,毕竟能利用完全二叉树特性的算法其性能都不会差。从上图中可以看出,分和合的二又树深度均为 l o g 2 n log_2n log2n,而每次分分合合的平均时间复杂度为O(n),二者相乘即为总的平均时间复杂度 O(nlog n),最好和最坏的情况都是一样的。
需要说明的是,归并排序属于稳定排序算法。

4. 算法代码

算法代码如下:
Python

# -*- coding: utf-8 -*-

# 归并排序
def merge (left, right) :
    """
    两个有序子序列的有序合并:
    依次对两个有序列表的最小数进行比较,较小的放入 result中;
    :param left: 左子序列
    :param right: 右子序列
    :return: 左右子序列所合成的有序序列
    """
    # result:存放已经排好顺序的数组
    result=[]
    #如果不符合左右子序列长度均大于0,则说明至少其中的一个数组已无数据
    while len(left) > 0 and len(right)>0:
        # 相等的时候优先把左侧的数放进结果列表,以保证其稳定性
        if left[0] <= right[0]:
            # list.pop (0)为移除并返回列表的第一个元素
            result.append (left.pop (0) )
        else:
            result.append(right.pop(0))
    # 跳出 while 循环后,我们便可以把另一个数组尽数加入 result 后面
    result += left
    result += right
    return result
def merge_sort(array) :
    '''
    无序序列的不断拆分:
    每次均由中间位置进行拆分,不断自我递归调用,直到子序列长度为1
    '''

    # 如果拆分后仅有单个元素,则返回该元素而不再拆分
    if len(array)== 1:
        return array
    #如果有两个及以上元素,则取中间位置进行拆分
    middle = len(array)// 2
    # 拆分后的左侧子串
    array_left = array[:middle]
    #拆分后的右侧子串
    array_right = array[middle:]
    # 对拆分后的左右子序列进行再拆分,直到 len(array) 1
    left = merge_sort(array_left)
    right = merge_sort(array_right)
    # 合并已拆分的左右子序列的同时进行排序并返回排序后的结果
    return merge(left, right)
#调用 merge_sort 函数
print(merge_sort([34,21,13,2,5,1,55,3,1,8]))

Java

  // 合并两个有序子数组的方法
    public static List<Integer> merge(List<Integer> left, List<Integer> right) {
        List<Integer> result = new ArrayList<>();

        while (!left.isEmpty() && !right.isEmpty()) {
            if (left.get(0) <= right.get(0)) {
                result.add(left.remove(0));
            } else {
                result.add(right.remove(0));
            }
        }

        // 将剩余元素全部添加到结果列表中
        while (!left.isEmpty()) {
            result.add(left.remove(0));
        }
        while (!right.isEmpty()) {
            result.add(right.remove(0));
        }

        return result;
    }

    // 归并排序主方法
    public static List<Integer> mergeSort(List<Integer> array) {
        if (array.size() == 1) {
            return array;
        }

        int middle = array.size() / 2;
        List<Integer> arrayLeft = new ArrayList<>(array.subList(0, middle));
        List<Integer> arrayRight = new ArrayList<>(array.subList(middle, array.size()));

        arrayLeft = mergeSort(arrayLeft);
        arrayRight = mergeSort(arrayRight);

        return merge(arrayLeft, arrayRight);
    }
        @Test
        void contextLoads () {
            List<Integer> array = Arrays.asList(34, 21, 13, 2, 5, 1, 55, 3, 1, 8);
            List<Integer> sortedArray = mergeSort(new ArrayList<>(array));
            System.out.println(sortedArray);
        }

5、输出结果

在这里插入图片描述

6. 算法过程分解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

忆梦九洲

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

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

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

打赏作者

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

抵扣说明:

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

余额充值