排序算法:归并排序

一、前言

    归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用。


二、算法思想

    该算法采用经典的分治(divide-and-conquer)策略(分治法将问题(divide)成一些小的问题然后递归求解,而(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

    动态效果示意图:
    在这里插入图片描述
    分而治之

    1、分阶段
    在这里插入图片描述
    可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。阶段可以理解为就是递归拆分子序列的过程,递归深度为logn。

    2、治阶段
    再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
    在这里插入图片描述
    在这里插入图片描述

C++代码:

#include <iostream>
#include <vector>
using namespace std;
 
void Merge(vector<int> &input, int left, int mid, int right, vector<int> temp){
    int i = left;                // i是第一段序列的下标
    int j = mid + 1;            // j是第二段序列的下标
    int k = 0;                    // k是临时存放合并序列的下标
    
    // 扫描第一段和第二段序列,直到有一个扫描结束
    while (i <= mid && j <= right){
        // 判断第一段和第二段取出的数哪个更小,将其存入合并序列,并继续向下扫描
        if (input[i] <= input[j]){
            temp[k++] = input[i++];
        }
        else{
            temp[k++] = input[j++];
        }
    }
    // 若第一段序列还没扫描完,将其全部复制到合并序列
    while (i <= mid){
        temp[k++] = input[i++];
    }
 
    // 若第二段序列还没扫描完,将其全部复制到合并序列
    while (j <= right){
        temp[k++] = input[j++];
    }
 
    k = 0;
    // 将合并序列复制到原始序列中
    while (left <= right){
        input[left++] = temp[k++];
    }
}
 
void MergeSort(vector<int> &input, int left, int right, vector<int> temp){
    if (left < right){
        int mid = (right + left) >> 1;
        MergeSort(input, left, mid, temp);
        MergeSort(input, mid + 1, right, temp);
        Merge(input, left, mid, right, temp);
    }
}
 
void mergesort(vector<int> &input){
    // 在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
    vector<int> temp(input.size());                
    MergeSort(input, 0, input.size() - 1, temp);
}
 
void main(){
    int arr[] = { 6, 4, 8, 9, 2, 3, 1};
    vector<int> test(arr, arr + sizeof(arr) / sizeof(arr[0]));
    cout << "排序前:";
    for (int i = 0; i < test.size(); i++){
        cout << test[i] << " ";
    }
    cout << endl;
    
    vector<int> result = test;
    mergesort(result);
    cout << "排序后:";
    for (int i = 0; i < result.size(); i++){
        cout << result[i] << " ";
    }
    cout << endl;
    system("pause");
}

运行结果如下图所示:
在这里插入图片描述
可以看到已经可以看到归并排序算法顺利执行了。

python代码:

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

def MergeSort(input_list):
    '''
    函数说明:归并排序(升序)
    Parameters:
        input_list - 待排序列表
    Returns:
        sorted_list - 升序排序好的列表
    '''

    def merge(input_list, left, mid, right, temp):
        '''
        函数说明:合并函数
        Parameters:
            input_list - 待合并列表
            left - 左指针
            right - 右指针
            temp - 临时列表
        Returns:
            无
        '''
        i = left
        j = mid + 1
        k = 0

        while i <= mid and j <= right:
            if input_list[i] <= input_list[j]:
                temp[k] = input_list[i]
                i += 1
            else:
                temp[k] = input_list[j]
                j += 1
            k += 1

        while i <= mid:
            temp[k] = input_list[i]
            i += 1
            k += 1
        while j <= right:
            temp[k] = input_list[j]
            j += 1
            k += 1

        k = 0
        while left <= right:
            input_list[left] = temp[k]
            left += 1
            k += 1

    def merge_sort(input_list, left, right, temp):
        if left >= right:
            return
        mid = (right + left) // 2
        merge_sort(input_list, left, mid, temp)
        merge_sort(input_list, mid + 1, right, temp)

        merge(input_list, left, mid, right, temp)

    if len(input_list) == 0:
        return []
    sorted_list = input_list
    temp = [0] * len(sorted_list)
    merge_sort(sorted_list, 0, len(sorted_list) - 1, temp)
    return sorted_list


if __name__ == '__main__':
    input_list = [6, 4, 8, 9, 2, 3, 1]
    print('排序前:', input_list)
    sorted_list = MergeSort(input_list)
    print('排序后:', sorted_list)

运行效果同上。


三、算法分析

1、归并排序算法的性能

在这里插入图片描述
其中,log2n为以2为底,n的对数。

2、时间复杂度

    归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的可以得出它的时间复杂度是O(n*log2n)

3、空间复杂度

    由前面的算法说明可知,算法处理过程中,需要一个大小为n的临时存储空间用以保存合并序列。

4、算法稳定性

    在归并排序中,相等的元素的顺序不会改变,所以它是稳定的算法。

5、归并排序和堆排序、快速排序的比较

    若从空间复杂度来考虑:首选堆排序,其次是快速排序,最后是归并排序。
    若从稳定性来考虑,应选取归并排序,因为堆排序和快速排序都是不稳定的。
    若从平均情况下的排序速度考虑,应该选择快速排序。


本文转载自:https://cuijiahua.com/blog/2018/01/algorithm_7.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值