归并排序示意图:
#自定义merge函数
def merge(L,R):
n = len(L) + len(R)
L.append(float("inf"))
R.append(float("inf"))
i = 0
j = 0
A = []
for k in range(0,n):
if L[i]<=R[j]:
A.append(L[i])
i = i+1
else:
A.append(R[j])
j = j+1
return A
merge([1,5,6],[2,3])
#自定义merge_sort函数
def merge_sort(A):
l = len(A)
if l<=1:
return A
else:
mid = l//2
print(mid)
left = merge_sort(A[0:mid])
right = merge_sort(A[mid:])
return merge(left,right)
#测试merge_sort函数
merge_sort([3,2,1,0,5,8,7,2,-5,9,6,11])
力扣88题:
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
归并排序利用了递归的思想,88题的题解代码如下,不算典型的归并排序,但是也运用了类似的归并排序思想,实际上是一种倒序排列的方法
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
i, j = m-1, n-1
curr = m+n-1
while curr >= 0:
num1 = nums1[i] if i >= 0 else float('-inf')
num2 = nums2[j] if j >= 0 else float('-inf')
if num1 >= num2:
nums1[curr] = num1
i -= 1
else:
nums1[curr] = num2
j -= 1
curr -= 1
return
剑指offer 51剑指offer51
归并思路:
「归并排序」与「逆序对」是息息相关的。归并排序体现了 “分而治之” 的算法思想,具体为:
分: 不断将数组从中点位置划分开(即二分法),将整个数组的排序问题转化为子数组的排序问题;
治: 划分到子数组长度为 1 时,开始向上合并,不断将 较短排序数组 合并为 较长排序数组,直至合并至原数组时完成排序;
合并阶段 本质上是 合并两个排序数组 的过程,而每当遇到 左子数组当前元素 > 右子数组当前元素 时,意味着 「左子数组当前元素 至 末尾元素」 与 「右子数组当前元素」 构成了若干 「逆序对」
算法流程:题解
class Solution:
def reversePairs(self, nums: List[int]) -> int:
def merge_sort(l, r):
# 终止条件
if l >= r: return 0
# 递归划分
m = (l + r) // 2
res = merge_sort(l, m) + merge_sort(m + 1, r)
# 合并阶段
i, j = l, m + 1
tmp[l:r + 1] = nums[l:r + 1]
for k in range(l, r + 1):
if i == m + 1:
nums[k] = tmp[j]
j += 1
elif j == r + 1 or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1
else:
nums[k] = tmp[j]
j += 1
res += m - i + 1 # 统计逆序对
return res
tmp = [0] * len(nums)
return merge_sort(0, len(nums) - 1)
剑指offer51题更好的一个解法新题解
class Solution:
def reversePairs(self, nums: List[int]) -> int:
def mergeSort(nums, low, high):
if low >= high: # 递归终止
return 0
ans = 0 # 记录当前逆序对数目
'''递归排序'''
mid = low+(high-low)//2
ans += mergeSort(nums, low, mid) # 左半部分逆序对数目
ans += mergeSort(nums, mid+1, high) # 右半部分逆序对数目
'''nums[low, mid] 和 nums[mid+1, high] 已排序好'''
tmp = [] # 记录nums[low, high]排序结果
left, right = low, mid+1
while left<=mid and right<=high:
if nums[left] <= nums[right]:
tmp.append(nums[left])
left += 1
else: # 后半部分值较小,出现了逆序
tmp.append(nums[right])
right += 1
ans += mid+1-left # 当前值 nums[right] 贡献的逆序对个数为 mid+1-left
'''解释:若nums[left] > nums[right],
则nums[left, mid] > nums[right]均成立,共 mid+1-left 项'''
'''左或右数组需遍历完(最多只有一个未遍历完)'''
while left<=mid:
tmp.append(nums[left])
left += 1
while right<=high:
tmp.append(nums[right])
right += 1
# ans += mid+1-left # 此时,前半部分一定已经遍历完了,即left=mid+1,因此无需再更新结果
nums[low:high+1] = tmp
return ans
'''主程序'''
return mergeSort(nums, 0, len(nums)-1)
这里while的用法比上一个的判断语句更加简洁
类似使用归并排序求解逆序对的题目:
力扣315题
题解为315题解
代码如下:
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
'''根据nums[*][0]进行排序,对应的index随之移动'''
def mergeSort(nums, low, high):
if low >= high: # 递归终止
return 0
'''递归排序'''
mid = low + (high-low)//2
mergeSort(nums, low, mid) # 左半部分逆序对数目
mergeSort(nums, mid+1, high) # 右半部分逆序对数目
'''nums[low, mid] 和 nums[mid+1, high] 已排序好'''
tmp = [] # 记录nums[low, high]排序结果
left, right = low, mid+1
while left<=mid and right<=high:
if nums[left][0] <= nums[right][0]: # 根据nums[*][0]进行排序
tmp.append(nums[left])
res[nums[left][1]] += right-(mid+1) # 记录逆序对数目【对应坐标nums[*][1]处】
left += 1
else:
tmp.append(nums[right])
right += 1
'''左或右数组需遍历完(最多只有一个未遍历完)'''
while left<=mid:
tmp.append(nums[left])
res[nums[left][1]] += right -(mid+1) # 记录逆序对数目【对应坐标nums[*][1]处】
left += 1
while right<=high:
tmp.append(nums[right])
right += 1
nums[low:high+1] = tmp
'''主程序'''
n = len(nums)
res = [0] * n # 存储结果
nums = [(num, idx) for idx, num in enumerate(nums)]
# 每个数值附上其对应的索引:
# 此时,nums[i][0]表示原来的数值,而nums[i][1]则表示原数值对应的索引(方便定位)
mergeSort(nums, 0, n-1) # 归并排序
return res