Это примерно так быстро, как я могу решить эту проблему сейчас
def find_dups_miss(arr)
groups = arr.group_by(&:itself)
arr.minmax.reduce(:upto).to_a - groups.keys << groups.select {|_,v| v.size > 1}.keys.sort
end
Пояснения основаны на опубликованном Array
Сначала мы группируем Array
элементы по себе
{10=>[10], 9=>[9, 9], 8=>[8], 6=>[6], 1=>[1], 2=>[2, 2], 4=>[4], 3=>[3, 3], 5=>[5, 5]}
Затем мы создаем Enumerator
из минимального и максимального (arr.minmax.reduce(:upto)
) значений из Array
, преобразуем его в Array
(to_a
) и вычитаем все keys
из предыдущей группы группировка:
arr.minmax.reduce(:upto).to_a - groups.keys
#=> [7]
Затем мы собираем все числа, которые встречались более одного раза в оригинале Array
: (Я отсортировал их, потому что желаемый результат был отсортирован)
groups.select {|_,v| v.size > 1}.keys.sort
#=> [2, 3, 5, 9]
и используйте Array#<<
, чтобы вставить Array
обратно в тот, который мы создали на предыдущем шаге, в результате
#=> [7,[2, 3, 5, 9]]
Если пропущен только один номер, то следующий текст немного быстрее, поскольку он не создает дополнительного Array
и коротких замыканий на первом пропущенном номере:
def find_dups_miss(arr)
groups = arr.group_by(&:itself)
[groups.select {|_,v| v.size > 1}.keys.sort].unshift(arr.minmax.reduce(:upto).find {|n| groups[n].nil?} )
end
Дополнительно для очень большого Array
:
groups.collect {|k,v| k if v.size > 1 }.compact.sort
кажется немного более эффективным, чем
groups.select {|_,v| v.size > 1}.keys.sort