这个特殊问题是如何选择正确算法的一个很好的例子,但更重要的是,正确的数据结构可以大大简化解决方案。事实上,在这种特殊情况下,选择正确的数据结构将使算法变得如此微不足道,以至于它基本上完全消失了:数据结构已经是答案。
我正在谈论的数据结构是Multiset:Multiset 就像Set,除了它不只存储唯一项目,而是存储每个项目在Multiset。基本上,Set 告诉您某个特定项目是否在 Set 中根本,此外,Multiset 还告诉您该特定项目的频率多久在Multiset.
很遗憾,Ruby 核心库或标准库中没有 Multiset 实现,但网络上流传着一些实现。
您实际上只需要从您的Array 构造一个Multiset。这是一个例子:
require 'multiset'
ary = ["student", "student", "teacher", "teacher", "teacher"]
print Multiset[*ary]
是的,仅此而已。这打印:
#2 "student"
#3 "teacher"
就是这样。例如,使用https://GitHub.Com/Josh/Multimap/:
require 'multiset'
histogram = Multiset.new(*ary)
# => #<Multiset: {"student", "student", "teacher", "teacher", "teacher"}>
histogram.multiplicity('teacher')
# => 3
例如,使用http://maraigue.hhiro.net/multiset/index-en.php:
require 'multiset'
histogram = Multiset[*ary]
# => #<Multiset:#2 'student', #3 'teacher'>
另一种可能性是使用Hash,这基本上只是意味着您必须自己做而不是Multiset 为您处理元素计数:
histogram = ary.inject(Hash.new(0)) {|hsh, item| hsh.tap { hsh[item] += 1 }}
print histogram
# { "student" => 2, "teacher" => 3 }
但是,如果您不计算自己,而是使用Enumerable#group_by 将元素自己分组,然后将分组映射到它们的大小,那么您可以更轻松。最后,转换回Hash:
Identity = ->x { x }
print Hash[[ary.group_by(&Identity).map {|n, ns| [n, ns.size] }]
# { "student" => 2, "teacher" => 3 }