二分法,详见csdn。时间beat 7%。或许就是这么慢吧,反正空间复杂度是O(1)就够了。
分析
两个数组各元素的和展开可形成一个矩阵,然后该题可转化为在"378. 有序矩阵中第 K 小的元素"。组成的矩阵形态如下:
(0,0), (0,1), ..., (0, l2-1)
(1,0), ...
(2,0), ..
...
(l1-1, 0), ...
其中矩阵元素(i,j)指num1[i]+nums2[j]。
答案
设第k小的数为pivot,需要留意的是,可能有多个两数和等于pivot的数,所以要先添加小于pivot的,再添加等于pivot的数。
class Solution:
def getRank(self, nums1, nums2, mid):
i, j = len(nums1) - 1, 0
rank = 0
while i >= 0 and j <= len(nums2) - 1:
if nums1[i] + nums2[j] <= mid:
rank += i + 1
j += 1
else:
i -= 1
return rank
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
# 找到第k小的和
left, right = nums1[0] + nums2[0], nums1[-1] + nums2[-1]
while left < right:
mid = left + (right - left) // 2
if self.getRank(nums1, nums2, mid) >= k:
right = mid
else:
left = mid + 1
# 输出结果
print(left)
res = []
for i in range(len(nums1)):
for j in range(len(nums2)):
if nums1[i] + nums2[j] >= left or len(res) == k:
break
res.append((nums1[i], nums2[j]))
for i in range(len(nums1)):
for j in range(len(nums2)):
if nums1[i] + nums2[j] < left:
continue
if nums1[i] + nums2[j] > left or len(res) == k:
break
res.append((nums1[i], nums2[j]))
return res
可以使用双向队列deque作更改,将等于pivot的数放在队头,当队满且遇到小于pivot的数时,可以将等于pivot的数出队(如果有的话)
class Solution:
def getRan
k(self, nums1, nums2, mid):
i, j = len(nums1) - 1, 0
rank = 0
while i >= 0 and j <= len(nums2) - 1:
if nums1[i] + nums2[j] <= mid:
rank += i + 1
j += 1
else:
i -= 1
return rank
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
# 找到第k小的和
left, right = nums1[0] + nums2[0], nums1[-1] + nums2[-1]
while left < right:
mid = left + (right - left) // 2
if self.getRank(nums1, nums2, mid) >= k:
right = mid
else:
left = mid + 1
# 输出结果
res = collections.deque()
equal_num = 0
for i in range(len(nums1)):
for j in range(len(nums2)):
if nums1[i] + nums2[j] < left:
if len(res) == k and equal_num > 0:
equal_num -= 1
res.popleft()
res.append((nums1[i], nums2[j]))
elif nums1[i] + nums2[j] == left:
if len(res) == k:
break
equal_num += 1
res.appendleft((nums1[i], nums2[j]))
else:
break