- 合并两个有序数组
class No_0088_Merge:
"""
题干:
https://leetcode.cn/problems/merge-sorted-array/
两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目
合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列
题目中要求合并后数组不应由函数返回,而是存储在数组nums1中, nums1预留了足够的0
解析:
两个数组分别用一个指针记录当前位置,比较指针位置的值,输出后指针向后移动。
例如:A=[*1,3,5], B=[*2,6,8], *号代表指针
1<2,输出:1, 状态:[1,*3,5], [*2,6,8]
2<3,输出:2, 状态:[1,*3,5], [2,*6,8]
3<6,输出:3, 状态:[1,3,*5], [2,*6,8]
5<6,输出:5, 状态:[1,3,5], [2,*6,8]
A已经完成,顺序输出B的剩余元素
"""
@staticmethod
def merge(nums1: list, m: int, nums2: list, n: int):
p0, p1, p2 = 0, 0, 0
_nums1 = nums1.copy()
while p1 < m and p2 < n:
if _nums1[p1] <= nums2[p2]:
nums1[p0] = _nums1[p1]
p1 += 1
else:
nums1[p0] = nums2[p2]
p2 += 1
p0 += 1
if p1 < m:
nums1[p0:] = _nums1[p1:m]
elif p2 < n:
nums1[p0:] = nums2[p2:n]
return nums1
@staticmethod
def merge_2(nums1: list, m: int, nums2: list, n: int):
p0, p1, p2 = m+n-1, m-1, n-1
while p1 >= 0 and p2 >= 0:
if nums1[p1] >= nums2[p2]:
nums1[p0] = nums1[p1]
p1 -= 1
else:
nums1[p0] = nums2[p2]
p2 -= 1
p0 -= 1
if p1 < 0:
nums1[:p0+1] = nums2[:p2+1]
elif p2 < 0:
nums1[:p0+1] = nums1[:p1+1]
return nums1
- 寻找两个正序数组的中位数
class No_0004_FindMedianSortedArrays:
"""
题干:
https://leetcode.cn/problems/median-of-two-sorted-arrays/
寻找两个正序数组的中位数
"""
@staticmethod
def findMedianSortedArrays(nums1: List[int], nums2: List[int]):
all_nums = nums1 + nums2
all_nums.sort()
size = len(all_nums)
if size < 2:
return all_nums[0]
elif size % 2 == 0:
return (all_nums[int(size/2)-1] + all_nums[int(size/2)]) / 2
else:
return all_nums[int(size/2)]
@staticmethod
def findMedianSortedArrays_2(nums1: List[int], nums2: List[int]):
"""
解析:
暴力解完全没有使用到数组有序的条件,可以参考88题合并有序数组, 时间复杂O(m+n)
实际上并不需需要完成数据的合并,当数组长度到达中位时就可以返回
"""
m, n = len(nums1), len(nums2)
mid = (m+n) / 2
p0, p1, p2 = -1, 0, 0
all_nums = [0] * (m+n)
while (p1 < m and p2 < n) and p1+p2 <= mid+1:
if nums1[p1] <= nums2[p2]:
all_nums[p0] = nums1[p1]
p1 += 1
else:
all_nums[p0] = nums2[p2]
p2 += 1
p0 += 1
if p1+p2 <= mid+1:
if p1 < m:
all_nums[p0:] = nums1[p1:]
elif p2 < n:
all_nums[p0:] = nums2[p2:]
if mid < 1:
return all_nums[0]
elif mid % 1 == 0:
return (all_nums[int(mid-1)] + all_nums[int(mid)]) / 2
else:
return all_nums[int(mid)]
@staticmethod
def findMedianSortedArrays_3(nums1: List[int], nums2: List[int]):
"""
划分数组:属于特定问题的特定解法,需要理解中位数的推导
从位置i将A划分为数量近似相等的两部分 A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1],称为A左集和A右集
从位置j将B划分为数量近似相等的两部分 B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1],称为B左集和B右集
分别合并左集和右集, 两种情况:
1、m+n为奇数:i+j = m-i + n-j + 1,∴ i+j = (m+n+1)/2,∴ j = (m+n+1)/2 - i,让
2、m+n为偶数 i+j = m-i + n-j,∴ i+j = (m+n)/2 ,整数除法可以补上1,值不变,这样和上面的式子就统一表达,可以忽略奇偶性
中位数的第二个性质,左集任意元素<=右集任意元素:
1、判断:A左集的最大元素 <= B右集合的最小值
2、判断:B左集的最大元素 <= A右集合的最小值
如果不满足的话,也要调整分割线
为减少处理分支,规定较短的数组为A,较长的数组为B,方面后续编码
前一部分的最小值,和后一部分的最大值,就对应了合并后的中位数
"""
m, n = len(nums1), len(nums2)
if m > n:
nums1, nums2 = nums2, nums1
m, n = n, m
tol_left = int((m + (n-m+1)) / 2)
left, right = 0, m
while left < right:
i = int(left + (right - left + 1)/2)
j = tol_left - i
if nums1[i-1] > nums2[j]:
right = i - 1
else:
left = i
i = left
j = tol_left-i
nums1_left_max = nums1[i-1] if i != 0 else float('-inf')
nums1_right_min = nums1[i] if i != m else float('inf')
nums2_left_max = nums2[j-1] if j != 0 else float('-inf')
nums2_right_min = nums2[j] if j != m else float('inf')
if (m+n) % 2 == 0:
return (max(nums1_left_max, nums2_left_max) + min(nums1_right_min, nums2_right_min))/2
else:
return max(nums1_left_max, nums2_left_max)