【算法日积月累】4-归并排序的 3 个优化角度

【算法日积月累】4-归并排序的 3 个优化角度

image-20190113002801072


归并排序的优化有以下 3 3 3 的角度:

1、如果两个数组,直接拼起来就是有序的,就无须 merge。即当 arr[mid]<=arr[mid+1]
的时候是不用 merge 的;

2、前面我们提到过,“插入排序”在小规模的排序任务上表现出色,这里,我们就可以在小区间里使用插入排序了;

3、我们每次做归并的时候,都 new 了辅助的空间,用完之后就丢弃了。事实上,我们可以全程使用 1 个和待排序数组一样长度的数组作为辅助归并两个排序数组的临时空间,这样就避免了频繁 new 和 delete 数组空间的操作。

1、数组有序直接合并

例如下面这两个数组,直接把它们接在一起就可以了。

image-20190112235809860

def __merge_sort(nums, left, right):
    if left >= right:
        return
    mid = left + (right - left) // 2  # 这是一个陷阱
    __merge_sort(nums, left, mid)
    __merge_sort(nums, mid + 1, right)
    if nums[mid] <= nums[mid + 1]:
        return
    __merge_of_two_sorted_array(nums, left, mid, right)

2、小区间排序使用“插入排序”

我们在介绍“插入排序”的时候,介绍了两种实现方式,我们不妨都实现一下。只不过,我们这里实现的是对原数组的子区间 [left, right] 使用插入排序。

实现1:

def insert_sort_for_merge_1(nums, left, right):
    """
    逐个向前交换的插入排序
    """
    # n = right - left + 1
    for i in range(left + 1, right + 1):
        for j in range(i, left, -1):  # 这里是 left
            if nums[j - 1] > nums[j]:
                nums[j], nums[j - 1] = nums[j - 1], nums[j]
            else:
                break

实现2:

def insert_sort_for_merge_2(nums, left, right):
    """
    多次赋值的插入排序
    """
    # n = right - left + 1
    for i in range(left + 1, right + 1):
        temp = nums[i]
        j = i - 1
        # 注意:这里 j 最多到 left
        while j >= left and nums[j] > temp:
            if nums[j] > temp:
                nums[j + 1] = nums[j]
                j -= 1
        nums[j + 1] = temp

把它们之一应用在递归终止条件:

def __merge_sort(nums, left, right):
    if right - left <= 15:
        insert_sort_for_merge_2(nums, left, right)
        return
    mid = left + (right - left) // 2  # 这是一个陷阱
    __merge_sort(nums, left, mid)
    __merge_sort(nums, mid + 1, right)
    if nums[mid] <= nums[mid + 1]:
        return
    __merge_of_two_sorted_array(nums, left, mid, right)

3、全局使用一个临时存储数组

这里我们直接给出完整的归并排序的代码:

def __merge_of_two_sorted_array(nums, left, mid, right):
    # 将原数组 [left,right] 区间内的元素复制到辅助数组
    for index in range(left, right + 1):
        nums_for_compare[index] = nums[index]

    # [1,  2, 3,   4,5]
    # left    mid    right
    i = left
    j = mid + 1
    for k in range(left, right + 1):
        if i == mid + 1:
            # i 用完了,就拼命用 j
            nums[k] = nums_for_compare[j]
            j += 1
        elif j > right:
            # j 用完了,就拼命用 i
            nums[k] = nums_for_compare[i]
            i += 1
        elif nums_for_compare[i] < nums_for_compare[j]:
            nums[k] = nums_for_compare[i]
            i += 1
        else:
            assert nums_for_compare[i] >= nums_for_compare[j]
            nums[k] = nums_for_compare[j]
            j += 1


def insert_sort_for_merge_1(nums, left, right):
    """
    逐个向前交换的插入排序
    """
    # n = right - left + 1
    for i in range(left + 1, right + 1):
        for j in range(i, left, -1):  # 这里是 left
            if nums[j - 1] > nums[j]:
                nums[j], nums[j - 1] = nums[j - 1], nums[j]
            else:
                break


def insert_sort_for_merge_2(nums, left, right):
    """
    多次赋值的插入排序
    """
    # n = right - left + 1
    for i in range(left + 1, right + 1):
        temp = nums[i]
        j = i - 1
        # 注意:这里 j 最多到 left
        while j >= left and nums[j] > temp:
            if nums[j] > temp:
                nums[j + 1] = nums[j]
                j -= 1
        nums[j + 1] = temp


def __merge_sort(nums, left, right):
    if right - left <= 15:
        insert_sort_for_merge_2(nums, left, right)
        return
    mid = left + (right - left) // 2  # 这是一个陷阱
    __merge_sort(nums, left, mid)
    __merge_sort(nums, mid + 1, right)
    if nums[mid] <= nums[mid + 1]:
        return
    __merge_of_two_sorted_array(nums, left, mid, right)


def merge_sort(nums):
    global nums_for_compare
    nums_for_compare = list(range(len(nums)))
    __merge_sort(nums, 0, len(nums) - 1)

分治思想的应用

1、计算数组的逆序对

《剑指 Offer》(第 2 版)第 51 题

2、在 LeetCode 上面搜索一下,看看还有哪些是分治思想解决的问题。

image-20190112204129392

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值