阅读 159

踏踏实实地打好算法基础(F)—归并排序(Merge Sort)

基本思想

将两个或两个以上的有序子序列归并为一个有序序列。在内部排序中,通常采用的是 2-路归并排序。

  • 将序列中待排序数字分为若干组,每个数字分为一个组
  • 将若干个组两两合并,保证合并后的组是有序的
  • 重复第二步操作直到只剩下一组,排序完成

合并两个序列

007.png

现在有两个有序的子序列,接下来就是要将这两个序列进行合并

005.png

首先分别在两个有序子序列设置一个指针,然后通过移动指针从有序子序列选取元素进行对比比较再放入到一个空序列中

006.png

比较子序列中的数字,例如 17 大于 4 那么就将 4 加入下面序列中,然后移动 4 位置指针向前(右侧)一个位置。右侧序列指针对应位置为 9 ,然后再比较 9 和 17 依然 17 大,所以将 9 放置下面序列中,并继续移动右侧指针,这时指针位置 25 大于 17 那么就将 17 放置到下面序列后,就开始移动左侧指针向前一个位置

008.png

通过重复上面步骤直到某一个指针来到一个有序数组最后元素并且将其放置到下面数组中后,如果另一个有序子序列还有剩余元素,因为剩余子元素已经是有序的,因此就可以一并将其移动到序列中。

001.png

从上面图可以看出整个排序也是先划分然后对子序列进行排序。

002.png

首先是划分过程将数组两两划分划分直至划分每个子序列只有一个元素为止

003.png

然后通过将这些元素进行组合,不断合并成为原有序列,合并过程将其调整为有序序列

代码实现

先实现如何将两个有序的子序列组合一个序列,组合后序列依然有序

def merge_two_sorted_lists(a,b):
    sorted_list = []

    return sorted_list

if __name__ == '__main__':
    a = [5,8,12,56]
    b = [7,9,45,51]

    print(merge_two_sorted_lists(a,b))
复制代码
def merge_two_sorted_lists(a,b):
    sorted_list = []
    len_a = len(a)
    len_b = len(b)

    i = j = 0

    while i < len_a and j < len_b:
        if a[i] <= b[j]:
            sorted_list.append(a[i])
            i += 1
        else:
            sorted_list.append(b[j])
            j += 1
    while i < len_a:
        sorted_list.append(a[i])
        i += 1
    while j < len_b:
        sorted_list.append(b[j])
        j += 1

    return sorted_list

if __name__ == '__main__':
    a = [5,8,12,56]
    b = [7,9,45,51]

    print(merge_two_sorted_lists(a,b))
复制代码

上面代码具有一定可读性,方法接收两个有序序列 a 和 b 作为参数,通过 i < len_a and j < len_b

009.png

010.png

011.png

012.png

上面图解释了当 j = 4 情况就无法进入循环,所以 56 无法放置到序列了。所以通过下面代码避免发生最后一个元素无法放置到序列中

    while i < len_a:
        sorted_list.append(a[i])
        i += 1
    while j < len_b:
        sorted_list.append(b[j])
        j += 1
复制代码

划分序列

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr)//2
    left = arr[:mid]
    right = arr[mid:]

    left = merge_sort(left)
    right = merge_sort(right)

    return merge_two_sorted_lists(left,right)
复制代码

这里用递归来通过

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr)//2
    left = arr[:mid]
    right = arr[mid:]

    left = merge_sort(left)
    right = merge_sort(right)

    return merge_two_sorted_lists(left,right)

def merge_two_sorted_lists(a,b):
    sorted_list = []
    len_a = len(a)
    len_b = len(b)

    i = j = 0

    while i < len_a and j < len_b:
        if a[i] <= b[j]:
            sorted_list.append(a[i])
            i += 1
        else:
            sorted_list.append(b[j])
            j += 1
    while i < len_a:
        sorted_list.append(a[i])
        i += 1
    while j < len_b:
        sorted_list.append(b[j])
        j += 1

    return sorted_list

if __name__ == '__main__':
    a = [5,8,12,56]
    b = [7,9,45,51]

    arr = [5,8,12,56,7,9,45,51]

    print(merge_sort(arr))
复制代码

不过上面的代码是开辟新的空间用于放置合并有序子序列,我们希望不开辟新的空间在原有数组上进行放置排好序的元素。

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr)//2
    left = arr[:mid]
    right = arr[mid:]

    # left = merge_sort(left)
    # right = merge_sort(right)
    merge_sort(left)
    merge_sort(right)

    return merge_two_sorted_lists(left,right,arr)



def merge_two_sorted_lists(a,b,arr):
    # sorted_list = []
    len_a = len(a)
    len_b = len(b)

    i = j = k = 0

    while i < len_a and j < len_b:
        if a[i] <= b[j]:
            arr[k] = a[i]
            # sorted_list.append(a[i])
            i += 1
        else:
            arr[k] = b[j]
            # sorted_list.append(b[j])
            j += 1
        k+=1
    while i < len_a:
        # sorted_list.append(a[i])
        arr[k] = a[i]
        i += 1
        k += 1
    while j < len_b:
        arr[k] = b[j]
        # sorted_list.append(b[j])
        j += 1
        k += 1

    # return sorted_list

if __name__ == '__main__':
    a = [5,8,12,56]
    b = [7,9,45,51]

    arr = [5,8,12,56,7,9,45,51]
    merge_sort(arr)
    print(arr)
复制代码
文章分类
后端