【算法日积月累】4-归并排序的 3 个优化角度
归并排序的优化有以下 3 3 3 的角度:
1、如果两个数组,直接拼起来就是有序的,就无须 merge。即当 arr[mid]<=arr[mid+1]
的时候是不用 merge 的;
2、前面我们提到过,“插入排序”在小规模的排序任务上表现出色,这里,我们就可以在小区间里使用插入排序了;
3、我们每次做归并的时候,都 new 了辅助的空间,用完之后就丢弃了。事实上,我们可以全程使用 1 个和待排序数组一样长度的数组作为辅助归并两个排序数组的临时空间,这样就避免了频繁 new 和 delete 数组空间的操作。
1、数组有序直接合并
例如下面这两个数组,直接把它们接在一起就可以了。
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 上面搜索一下,看看还有哪些是分治思想解决的问题。