归并排序详解(附python实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HappyRocking/article/details/83145337

一、介绍

归并排序(Merge Sort)指的是利用分治和递归的思想,对一个乱序的数列进行排序。

  • 所谓“分”,指的是将一个乱序数列不断进行二分,得到许多短的序列。
  • 所谓“治”,指的是将这些短序列进行两两合并,然后将合并的结果作为新的序列,再与其他序列进行合并,最终得到一个新的序列。

因此,归并排序具体包括两个步骤:分散、和并。

二、具体步骤

分散和合并的示例参见下图:
在这里插入图片描述

1、分散(从整到零)

将原始序列一刀切开,划分成两个序列。然后每个序列继续切开,又划分成两个更小的序列。

这样一层层地划分,可以得到一颗划分的树,树的叶子节点就是原始序列的每个元素了。

2、合并(从零到整)

从树的叶子节点开始沿着枝干往上合并,序列数越来越少,每个序列的长度越来越大,最终剩下一个序列,这个序列即为所求。

这时候注意,每次合并时,输入的是两个有序数列,输出的是一个合并之后的有序数列。

为什么可以保证输入的是有序的呢?

可以使用数学归纳法来证明。在合并的起始阶段,每个序列就是一个元素,自然是有序的。然后进行正确的合并操作之后,输出的也是有序的。接下来以这个输出的序列为输入继续合并,自然输出也是有序的。因此,最终得到的数列,也是有序的。

那么,关键就在于正确的合并操作是怎么操作了。比如有两个有序的数列 l 1 l1 l 2 l2 ,想要对其进行合并,可以:

1、定义两个指针,分别指向两个数列的第一个元素;定义新的空数列,作为结果。
2、依次取出指针值,比较大小,将较小值追加到新数列,同时将较小值的指针往后移动一位。
3、如果其中一个指针到头了,那么将另一个指针剩下的数列直接追加到结果数列即可。
4、直至两个指针都指到了最后一位。

这样,得到的结果数列便是有序数列。

三、复杂度分析

时间复杂度

由“分散”和“合并”两部分组成。

“分散”:最后分成一个个的单一元素,因此复杂度为 O ( n ) O(n)

“合并”:由于每次进行两两合并,因此合并的次数为 O ( l o g 2 n ) O(log_2n) ,而每次合并的复杂度为 O ( n ) O(n) ,因此整体合并的复杂度为 O ( n l o g 2 n ) O(nlog_2n)

因此,整个算法的时间复杂度为 O ( n l o g 2 n ) O(nlog_2n)

空间复杂度

归并的空间复杂度就是那个临时的数组和递归时压入栈的数据占用的空间: n + l o g n n + logn ;所以空间复杂度为 O ( n ) O(n)

python实现

def merge_sort(lists):
    '''
    递归进行归并排序。
    '''
    # 递归结束条件
    if len(lists) <= 1:
        return lists
    
    # 分治进行递归
    middle = len(lists)//2
    left = merge_sort(lists[:middle])
    right = merge_sort(lists[middle:])
    
    # 将两个有序数组进行合并
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        # 将较小值放入到result中
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    
    # 将未被扫描到的直接追加到result后面
    if i == len(left):
        result.extend(right[j:])
    else:
        result.extend(left[i:])
    
    return result
    
if __name__ == '__main__':
    a = [2, 6, 10, 3, 5, 8, 4]
    print(merge_sort(a))

猜你喜欢

转载自blog.csdn.net/HappyRocking/article/details/83145337
今日推荐