我有一个类似 [1,1,1,2,4,6,3,3] 的数组,我想获取重复元素的列表,在本例中为 [1,3]。我写了这个:
my_array.select{|obj|my_array.count(obj)>1}.uniq
但可悲的是效率低下(o(n²))。你有更好的主意吗?如果可能的话简明扼要。
谢谢
灵感来自 Ilya Haykinson 的回答:
def repeated(array)
counts = Hash.new(0)
array.each{|val|counts[val]+=1}
counts.reject{|val,count|count==1}.keys
end
使用 Ruby 的Set库:
require 'set'
ary = [1,1,1,2,4,6,3,3]
dups = Set.new
test_set = Set.new
ary.each {|val| dups.add(val) unless test_set.add?(val)}
dups.to_a # [1, 3]
我相信这应该是 O(n),因为 Set#add 和 Set#add? 据我所知,是恒定时间操作。
这样的事情怎么样?它将在 O(n) 中运行。
a = [1,1,1,2,4,6,3,3]
b = {}
a.each { |v| if b.has_key? v then b[v] = b[v]+1 else b[v]=1 end }
b.reject { |k,v| if v > 1 then false else true end }.keys
AO(n) 解决方案(更改<< x
为+ [x]
并update
使其merge
成为纯功能):
rs = xs.inject([[], {}]) do |(out, seen), x|
[(seen[x] == 1 ? (out << x) : out), seen.update(x => (seen[x] || 0)+1)]
end[0]
一种更简单但空间效率更低的方法:
rs = xs.group_by { |x| x }.select { |y, ys| ys.size > 1 }.keys
使用“列表理解”避免中间散列的相同想法:
rs = xs.group_by { |x| x }.map { |y, ys| y if ys.size > 1 }.compact
使用inject
[1,1,1,2,4,6,3,3].inject({}){ |ele, n| ele[n] = nil; ele }.keys
# => [1, 2, 4, 6, 3]
ele
hash 它被初始化为{}
,每次迭代都会将一个带有数字n
和nil
值的键添加到ele
哈希中。最后ele
返回为:
{1=>nil, 2=>nil, 4=>nil, 6=>nil, 3=>nil}
我们只想要钥匙,所以.keys
结束工作。
一些想法:您必须找出正确的库数据结构:
1对数组排序O(nlogn),然后遍历数组
2创建一个集合,在集合中搜索当前数组元素,如果没有找到,插入并继续查找所有元素 - 再次 O(nlogn)。
我正在考虑计算一个唯一元素在数组中出现的次数。就像最初的建议一样,它可能真的效率低下,但看这个问题很有趣。我没有在更大的阵列上做任何基准测试,所以这只是一个练习。
a = [1,1,1,2,4,6,3,3]
dupes = []
a.uniq.each do |u|
c = a.find_all {|e| e == u}.size
dupes << [u, c] unless c == 1
end
puts dupes.inspect
# dupes = [[1, 3], [3, 2]]
# 1 appears 3 times
# 3 appears twice
# to extract just the elment a bit cleaner
dupes = a.uniq.select do |u|
a.find_all {|e| e == u}.size != 1
end
puts dupes.inspect
# returns [1,3]
如果重复的条目总是连续的,这将起作用,如您的示例所示;否则你必须先排序。each_cons 检查指定大小的滚动窗口。
require 'set'
my_array = [1,1,1,2,4,6,3,3]
dups = Set.new
my_array.each_cons(2) {|a,b| dups.add(a) if (a == b)}
p dups.to_a