持续维护中...
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
该问题最简单的方式就是直接对 arr 进行排序,然后取前 k 个数值。但其实有时间复杂度更优的方案,就是部分排序,只是找到前 k 个最小值即可。
def findSmallKnum(arr, k):
for i in range(k):
for j in range(i+1, len(arr)):
if arr[i] > arr[j]:
arr[i], arr[j] = arr[j], arr[i]
return arr[:k]
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数 。算法的时间复杂度应该为 O(log(m+n)) 。
示例:
输入:nums1 = [1,3], nums2 = [2]
输出:2? ? ? 解释:合并数组 = [1,2,3] ,中位数 2
该问题可以转换为求两个有序数组的第 k 最小值问题,需要用到一个第 k 最小值规律。这个规律为针对两个有序数组 A 和 B,如果 A 中的第 m 个数据 A(m-1) 大于 B 中的第 n 个数据 B(n-1),那可以保证两个有序数组的总数组 C 的第 m+n 个数据 C(m+n-1) 一定大于 B(n-1) 。可以用反正法证明,如果?C(m+n-1) 小于等于 B(n-1),那数组 B 中小于 C(m+n-1) 的数据个数一定小于等于 n-1,数组 A 中小于 C(m+n-1) 的数据个数一定小于等于 m-1,所以在总数据 C 中小于 C(m+n-1) 的个数小于 m+n-2 (m-1 与 n-1 不能同时获取),而实际情况是小于?C(m+n-1) 的数据个数是 m+n-1,所以证明结束。所以解该题目的思路为,假定在总数组中中位数是第 k 个数据,那就分别在 nums1 和 nums2 中比较第 k/2 个数值,然后基于第 k 最小值规律可以从某数组中剔除 k/2 个数据,然后接下来继续寻找剩余两个数组的第 k/2 最小值,以此类推,寻找 k/4、k/8、k/16...,直至 1 或者某数组中所有数据均已被剔除,所以时间复杂度可以做到 O(log(m+n))。但实际编程中需要考虑 k 是整数并非浮点数,如何取整和处理边界条件,以及数据索引与数据个数之间的关系。
def findMedianSortedArrays(nums1, nums2):
def getK2(k): # 定义一个函数实现查询的第 k 和 k+1 大的值,k 值不能太大
id1, id2 = 0, 0 # 数组 1 和 2 的寻找起始索引,包含当前索引位置
while True: # id1 和 id2 是起始索引,k 是第几个值,所以 k 在变索引时需要减一
# 终止条件
if id1 == m: # 数组 1 中的值已被全部剔除
return nums2[id2+k-1], nums2[id2+k] # 在数组 2 中寻找起于 id2 的第 k 个值,k 变索引需要减一
if id2 == n: # 数组 2 中的值已被全部剔除
return nums1[id1+k-1], nums1[id1+k] # 在数组 1 中寻找起于 id1 的第 k 个值,k 变索引需要减一
if k == 1: # k 等于 1 时,第 k 最小值,只需要找 min(nums1[id1],nums2[id2])
if nums1[id1] < nums2[id2]: # 但第 k+1 个值比较麻烦,需要增加一些判断条件,分类处理
if id1 == m-1:
return nums1[id1], nums2[id2]
else:
return nums1[id1], min(nums1[id1+1], nums2[id2])
else:
if id2 == n-1:
return nums2[id2], nums1[id1]
else:
return nums2[id2], min(nums2[id2+1], nums1[id1])
# 二分法遍历
newid1 = min(id1+k//2-1, m-1) # 数组 1 需要判断的数值索引,对 k 的奇偶没有要求,但必须减一。如果不减一,会造成多剔除数据,漏掉结果,原因是 k//2 是第几个数不是索引
newid2 = min(id2+k//2-1, n-1) # 变成索引需要减一。也不能减 2,原因是 k 大于等于 2,当 K==2 时,k//2-2 变负值了,其需要大于等于零
p1, p2 = nums1[newid1], nums2[newid2] # 根据第 k 最小值订定理,其实上面语句需要保证 [(id1+k//2-x)+1]+[(id2+k//2-x)+1] 小于等于 id1+id2+k,所以 x>=1,这用来解释为何减一,这是本代码最难的部分!!!
if p1 <= p2: # 若数组 1 的当前判断值较小,则说明判断下标以及之前的值全部都在中值左边,全部排除
k -= newid1 - id1 + 1 # k 需要减去之前剔除的值的数量
id1 = newid1 + 1 # 数组 1 从去除的索引后一位重新开始
else: # 同理
k -= newid2 - id2 + 1
id2 = newid2 + 1
m, n = len(nums1), len(nums2) # 数组 1,数组 2 的长度
Length = m + n # 两个数组总长度
v1, v2 = getK2(Length//2) # 通过 getK2 函数获取 Length//2 和 Length//2+1 对应的数值
if Length%2 == 1: # 若总数组长度为奇数,则使用 Length//2+1 对应的数值
return v2
else: # 若总数组长度为偶数,则使用 Length//2 和 Length//2+1 对应数值的均值
return (v1+v2)/2