归并排序详解(python实现)

15 篇文章 1 订阅
7 篇文章 1 订阅

因为上个星期leetcode的一道题(Median of Two Sorted Arrays)所以想仔细了解一下归并排序的实现。

还是先阐述一下排序思路:

首先归并排序使用了二分法,归根到底的思想还是分而治之。拿到一个长数组,将其不停的分为左边和右边两份,然后以此递归分下去。然后再将她们按照两个有序数组的样子合并起来。这样说起来可能很难理解,于是给出一张我画的图。

这里显示了归并排序的第一步,将数组按照middle进行递归拆分,最后分到最细之后再将其使用对两个有序数组进行排序的方法对其进行排序。

两个有序数组排序的方法则非常简单,同时对两个数组的第一个位置进行比大小,将小的放入一个空数组,然后被放入空数组的那个位置的指针往后 移一个,然后继续和另外一个数组的上一个位置进行比较,以此类推。到最后任何一个数组先出栈完,就将另外i一个数组里的所有元素追加到新数组后面。

由于递归拆分的时间复杂度是logN 然而,进行两个有序数组排序的方法复杂度是N该算法的时间复杂度是N*logN 所以是NlogN。

 

根据这波分析,我们可以看看对上图的一个行为。

当最左边的分到最细之后无法再划分左右然后开始进行合并。

第一次组合完成[4, 7]的合并

第二次组合完成[4, 7, 8]的合并

第三次组合完成[3, 5]的合并

第四次组合完成[3, 5, 9]的合并

第五次组合完成[3, 4, 5, 7, 8, 9]的合并结束排序。

 

归并排序:
    先分开再合并,分开成单个元素,合并的时候按照正确顺序合并
    
    假如我们有一个n个数的数列,下标从0到n-1
  首先是分开的过程
    1 我们按照 n//2 把这个数列分成两个小的数列
    2 把两个小数列 再按照新长度的一半 把每个小数列都分成两个更小的
    。。。一直这样重复,一直到每一个数分开了
    比如:    6 5 4 3 2 1
        第一次 n=6 n//2=3 分成      6 5 4      3 2 1
        第二次 n=3 n//2=1 分成    6   5 4    3   2 1
        第三次 n=1的部分不分了
                n=2 n//2=1 分成     5   4      2  1
                
    之后是合并排序的过程:
    3 分开之后我们按照最后分开的两个数比较大小形成正确顺序后组合绑定
        刚刚举得例子 最后一行最后分开的数排序后绑定   变成     4 5     1 2
        排序后倒数第二行相当于把最新分开的数排序之后变成    6   4 5       3    12
    4 对每组数据按照上次分开的结果,进行排序后绑定
        6 和 4 5(两个数绑定了)  进行排序
        3 和 1 2(两个数绑定了)  进行排序
        排完后 上述例子第一行待排序的  4 5 6      1 2 3  两组数据
    5 对上次分开的两组进行排序
        拿着 4 5 6     1 2 3两个数组,进行排序,每次拿出每个数列中第一个(最小的数)比较,把较小的数放入结果数组。再进行下一次排序。
        每个数组拿出第一个数,小的那个拿出来放在第一位 1 拿出来了,   变成4 5 6    2 3
        每个数组拿出第一个书比较小的那个放在下一个位置  1 2被拿出来,  待排序 4 5 6      2
        每个数组拿出第一个书比较小的那个放在下一个位置  1 2 3 被拿出来,  待排序 4 5 6
        如果一个数组空了,说明另一个数组一定比排好序的数组最后一个大 追加就可以结果 1 2 3 4 5 6
    相当于我们每次拿到两个有序的列表进行合并,分别从两个列表第一个元素比较,把小的拿出来,在拿新的第一个元素比较,把小的拿出来
        这样一直到两个列表空了 就按顺序合并了两个列表
    
    结束

时间复杂度: 最好最坏都是 O( n log n )
稳定性:稳定
缺点:每次拆分数组都要开心的数组, 每次合并数组都要开新数组,空间复杂度很大

def merge(a, b):
    c = []
    h = j = 0
    while j < len(a) and h < len(b):
        if a[j] < b[h]:
            c.append(a[j])
            j += 1
        else:
            c.append(b[h])
            h += 1

    if j == len(a):
        for i in b[h:]:
            c.append(i)
    else:
        for i in a[j:]:
            c.append(i)

    return c


def merge_sort(lists):
    if len(lists) <= 1:
        return lists
    middle = len(lists)/2
    left = merge_sort(lists[:middle])
    right = merge_sort(lists[middle:])
    return merge(left, right)


if __name__ == '__main__':
    a = [4, 7, 8, 3, 5, 9]
    print merge_sort(a)

 

 

合并两个已排序数组

 

将两个已排序数组,合并成另一个。

先要条件:两个数组元素已经有序;数组大小不定。

最后结果:形成一个新数组,包含两个数组的所有元素,并且,新数组是有序的。

public static void main(String[] args) {        
        int[] data1 = {1, 3, 5, 7};
        int[] data2 = {2, 4, 6, 8};
        int[] dataMerged = mergeArray(data1, data2);
        for (int i = 0; i < dataMerged.length; i++) {
            System.out.println(i + " : " + dataMerged[i]);
        }
    }
    
    public static int[] mergeArray(int[] array1, int[] array2) {
        int length = array1.length + array2.length;
        int[] arrayMerged = new int[length];
        int i = 0;
        int j = 0;
        int k = 0;
        
        int length1 = array1.length;
        int length2 = array2.length;
        while (i< length1 && j < length2) {
            if (array1[i] < array2[j]) {            //拷贝值较小的元素,填充合并数组
                arrayMerged[k++] = array1[i++];
            } else if (array1[i] > array2[j]) {
                arrayMerged[k++] = array2[j++];
            } else {
                arrayMerged[k++] = array1[i++];
                arrayMerged[k++] = array1[j++];
            }
        }

        if (i >= length1) {
            while (j < length2) {
                arrayMerged[k++] = array2[j++];
            }
        } else {
            while (i < length1) {
                arrayMerged[k++] = array1[i++];
            }
        }
        
        return arrayMerged;
    }

  • 10
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值