是的,这是可能的!我已经修改了你的示例代码来做到这一点。
我的回答假设您的问题是关于算法的 - 如果您想要使用 sets 的运行速度最快的代码,请查看其他答案。
这维持了O(n log(k)) 的时间复杂度:if lowest != elem or ary != times_seen: 和unbench_all = False 之间的所有代码都是O(log(k))。主循环(for unbenched in range(times_seen):)内部有一个嵌套循环,但这只运行times_seen 次,times_seen 最初为 0,每次运行此内部循环后重置为 0,并且只能递增一次每个主循环迭代,所以内循环不能比主循环做更多的迭代。因此,由于内循环内的代码是O(log(k)),并且最多运行与外循环一样多的次数,外循环是O(log(k))并运行n次,所以算法是O(n log(k))。
此算法依赖于 Python 中元组的比较方式。它比较元组的第一项,如果它们相等,则比较第二项(即(x, a) < (x, b) 为真当且仅当a < b)。
在这个算法中,与问题中的示例代码不同,当一个项目从堆中弹出时,它不一定在同一次迭代中再次被推送。由于我们需要检查所有子列表是否包含相同的数字,所以在从堆中弹出一个数字后,它的子列表就是我所说的“benched”,意思是它不会被添加回堆中。这是因为我们需要检查其他子列表是否包含相同的项目,所以现在不需要添加这个子列表的下一个项目。
如果一个数字确实在所有子列表中,那么堆看起来像[(2,0),(2,1),(2,2),(2,3)],元组的所有第一个元素都相同,所以heappop 将选择具有最低子列表的那个指数。这意味着第一个索引 0 将被弹出,times_seen 将增加为 1,然后索引 1 将被弹出,times_seen 将增加为 2 - 如果 ary 不等于 times_seen,则数字为不在所有子列表的交集处。这会导致条件if lowest != elem or ary != times_seen:,它决定何时不应在结果中出现数字。这个if 语句的else 分支用于当它仍然可能在结果中时。
unbench_all 布尔值适用于需要从工作台中删除所有子列表的情况 - 这可能是因为:
- 已知当前号码不在子列表的交集内
- 已知在子列表的交集
当unbench_all 为True 时,所有从堆中删除的子列表都会重新添加。众所周知,这些是在range(times_seen) 中具有索引的那些,因为该算法仅在它们具有相同编号时才从堆中删除项目,因此它们必须按索引顺序连续删除,并且从索引 0 开始,并且有必须是其中的times_seen。这意味着我们不需要存储 benched 子列表的索引,只需要存储已被 benched 的数字。
import heapq
def mergeArys(srtd_arys):
heap = []
srtd_iters = [iter(x) for x in srtd_arys]
# put the first element from each srtd array onto the heap
for idx, it in enumerate(srtd_iters):
elem = next(it, None)
if elem:
heapq.heappush(heap, (elem, idx))
res = []
# the number of tims that the current number has been seen
times_seen = 0
# the lowest number from the heap - currently checking if the first numbers in all sub-lists are equal to this
lowest = heap[0][0] if heap else None
# collect results in nlogK time
while heap:
elem, ary = heap[0]
unbench_all = True
if lowest != elem or ary != times_seen:
if lowest == elem:
heapq.heappop(heap)
it = srtd_iters[ary]
nxt = next(it, None)
if nxt:
heapq.heappush(heap, (nxt, ary))
else:
heapq.heappop(heap)
times_seen += 1
if times_seen == len(srtd_arys):
res.append(elem)
else:
unbench_all = False
if unbench_all:
for unbenched in range(times_seen):
unbenched_it = srtd_iters[unbenched]
nxt = next(unbenched_it, None)
if nxt:
heapq.heappush(heap, (nxt, unbenched))
times_seen = 0
if heap:
lowest = heap[0][0]
return res
if __name__ == '__main__':
a1 = [[1, 3, 5, 7], [1, 1, 3, 5, 7], [1, 4, 7, 9]]
a2 = [[1, 1], [1, 1, 2, 2, 3]]
for arys in [a1, a2]:
print(mergeArys(arys))
如果您愿意,可以这样编写等效算法:
def mergeArys(srtd_arys):
heap = []
srtd_iters = [iter(x) for x in srtd_arys]
# put the first element from each srtd array onto the heap
for idx, it in enumerate(srtd_iters):
elem = next(it, None)
if elem:
heapq.heappush(heap, (elem, idx))
res = []
# collect results in nlogK time
while heap:
elem, ary = heap[0]
lowest = elem
keep_elem = True
for i in range(len(srtd_arys)):
elem, ary = heap[0]
if lowest != elem or ary != i:
if ary != i:
heapq.heappop(heap)
it = srtd_iters[ary]
nxt = next(it, None)
if nxt:
heapq.heappush(heap, (nxt, ary))
keep_elem = False
i -= 1
break
heapq.heappop(heap)
if keep_elem:
res.append(elem)
for unbenched in range(i+1):
unbenched_it = srtd_iters[unbenched]
nxt = next(unbenched_it, None)
if nxt:
heapq.heappush(heap, (nxt, unbenched))
if len(heap) < len(srtd_arys):
heap = []
return res