LeetCode 1439
求的是k个最小值,那么有一个可以考虑的方法就是维护一个堆,然后把最小的k-1一个pop掉。只是怎么来维护进堆的顺序。
因为每行都是已经排序的,第一个组合应该是取每行中的第一个,然后可以考虑把各行前进一格的放入堆中,取出最小的一个,然后再把隔行的下一个组合放入堆中。但是要考虑重复的情况。所以如果访问过了,我们就不再加入堆。
def kthSmallest(self, mat: List[List[int]], k: int) -> int:
#维护一个最小堆
minh = []
#有m行
m = len(mat)
#n列
n = len(mat[0])
#当前每行取的位置,第一个组合,我们去的全部是第一个位置
index = [0]*m
#求出index所在的和
def getSum(mat, index):
total = 0
m = 0
for i in index:
total += mat[m][i]
m+=1
return total
#把index转为10进制数,方便加入set用于校验是否已经访问过
def convert(index):
ci = 0
for i in index:
ci = ci*10 + i
return ci
visited = set()
visited.add(convert(index))
collections._heapq.heappush(minh, (getSum(mat, index), index))
while k > 0 and len(minh) > 0:
current = collections._heapq.heappop(minh)
k -=1
index = current[1]
#移动其中的一行,加入堆
for i in range(m):
if index[i] == n-1:
continue
ic = index.copy()
ic[i] +=1
#如果已经访问过,那么不再加入
if convert(ic) in visited:
continue
collections._heapq.heappush(minh, (getSum(mat, ic), ic))
visited.add(convert(ic))
return current[0]
这个方法思路比较简单,但是整体效率不高,因为我们还要做去重。
另外一个方法是利用:
https://blog.csdn.net/frankguodongchen/article/details/105760871
的结果,每次组合两行,求出最小的k个组合,然后继续和下一行进行组合。
def kthSmallest(self, mat: List[List[int]], k: int) -> int:
minh = []
m = len(mat)
n = len(mat[0])
ans = mat[0]
for m in mat[1:]:
ans = self.kSmallestPairs(ans, m, k)
return ans[k-1]
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
if not nums1 or not nums2:
return []
M = len(nums1)
N = len(nums2)
heapSize = min(k, N)
minHeap = [(nums1[0] + nums2[i], 0, i) for i in range(heapSize)]
heapq.heapify(minHeap)
result = []
while minHeap and k > 0:
val, r, c = heapq.heappop(minHeap)
k -=1
result.append(val)
if (r < M-1):
heapq.heappush(minHeap, (nums1[r+1] + nums2[c], r+1, c))
return result