如何在Ruby中懒惰地合并N个排序数组(或其他类似列表的数据结构)?例如,在Python中,您将使用heapq.merge。 Ruby中必须有这样的内容,对吧?
答案 0 :(得分:1)
这是一个(稍微高尔夫)的解决方案,应该适用于支持#first
,#shift
和#empty?
的任何“类似列表”的集合的数组。请注意,它具有破坏性 - 每次调用lazymerge
都会从一个集合中删除一个项目。
def minheap a,i
r=(l=2*(m=i)+1)+1 #get l,r index
m = l if l< a.size and a[l].first < a[m].first
m = r if r< a.size and a[r].first < a[m].first
(a[i],a[m]=a[m],a[i];minheap(a,m)) if (m!=i)
end
def lazymerge a
(a.size/2).downto(1){|i|minheap(a,i)}
r = a[0].shift
a[0]=a.pop if a[0].empty?
return r
end
p arrs = [ [1,2,3], [2,4,5], [4,5,6],[3,4,5]]
v=true
puts "Extracted #{v=lazymerge (arrs)}. Arr= #{arrs.inspect}" while v
输出:
[[1, 2, 3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 1. Arr= [[2, 3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 2. Arr= [[3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 2. Arr= [[4, 5], [3], [4, 5, 6], [3, 4, 5]]
Extracted 3. Arr= [[4, 5], [3, 4, 5], [4, 5, 6]]
Extracted 3. Arr= [[4, 5], [4, 5], [4, 5, 6]]
Extracted 4. Arr= [[5], [4, 5], [4, 5, 6]]
Extracted 4. Arr= [[5], [5], [4, 5, 6]]
Extracted 4. Arr= [[5, 6], [5], [5]]
Extracted 5. Arr= [[6], [5], [5]]
Extracted 5. Arr= [[5], [6]]
Extracted 5. Arr= [[6]]
Extracted 6. Arr= [[]]
Extracted . Arr= [[]]
另请注意,此算法在保持堆属性方面也很懒惰 - 在调用之间不会保留它。这可能导致它完成比需要更多的工作,因为它在每次后续调用时都会完成堆积。这可以通过预先完成一次完整的堆化,然后在minheap(a,0)
行之前调用return r
来解决。
答案 1 :(得分:1)
我最终使用'algorithm'gem中的数据结构自己编写了它。它没有我想象的那么糟糕。
require 'algorithms'
class LazyHeapMerger
def initialize(sorted_arrays)
@heap = Containers::Heap.new { |x, y| (x.first <=> y.first) == -1 }
sorted_arrays.each do |a|
q = Containers::Queue.new(a)
@heap.push([q.pop, q])
end
end
def each
while @heap.length > 0
value, q = @heap.pop
@heap.push([q.pop, q]) if q.size > 0
yield value
end
end
end
m = LazyHeapMerger.new([[1, 2], [3, 5], [4]])
m.each do |o|
puts o
end
答案 2 :(得分:1)
这是一个应该适用于任何Enumerable,甚至是无限的实现。它返回Enumerator。
def lazy_merge *list
list.map!(&:enum_for) # get an enumerator for each collection
Enumerator.new do |yielder|
hash = list.each_with_object({}){ |enum, hash|
begin
hash[enum] = enum.next
rescue StopIteration
# skip empty enumerators
end
}
loop do
raise StopIteration if hash.empty?
enum, value = hash.min_by{|k,v| v}
yielder.yield value
begin
hash[enum] = enum.next
rescue StopIteration
hash.delete(enum) # remove enumerator that we already processed
end
end
end
end
Infinity = 1.0/0 # easy way to get infinite range
p lazy_merge([1, 3, 5, 8], (2..4), (6..Infinity), []).take(12)
#=> [1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10]
答案 3 :(得分:0)
不,没有内置的东西可以做到这一点。至少,没有什么可以立即浮现在脑海中。但是,几年前有一个GSoC project来实现相关数据类型,您可以使用它们。