كيف يمكنني استخراج العناصر المتكررة بكفاءة في صفيف روبي؟ [مكرر
سؤال
هذا السؤال لديه بالفعل إجابة هنا:
لدي مجموعة مثل [1،1،1،2،4،6،3،3،3] وأرغب في الحصول على قائمة العناصر المتكررة، في هذه الحالة [1،3]. انا كتبت هذا:
my_array.select{|obj|my_array.count(obj)>1}.uniq
لكنه غير فعال بشكل مأساوي (O (N²)). هل تمتلك فكرة افضل؟ إذا كان ذلك ممكنا موجزة.
شكرًا
المحلول
مستوحاة من إجابة إليا هايكينسون:
def repeated(array)
counts = Hash.new(0)
array.each{|val|counts[val]+=1}
counts.reject{|val,count|count==1}.keys
end
نصائح أخرى
باستخدام روبي جلس مكتبة:
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)، لأن مجموعة # إضافة وتعيين # إضافة؟ هي عمليات ثابتة، بقدر ما أعرف.
ماذا عن شيء مثل هذا؟ سوف يعمل في O (ن).
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
التجزئة تم تخصيصها {}
, ، كل تكرار مفتاح مع الرقم n
و nil
تتم إضافة القيمة إلى ele
التجزئة. في نهايةالمطاف ele
يتم إرجاع كما:
{1=>nil, 2=>nil, 4=>nil, 6=>nil, 3=>nil}
نريد فقط المفاتيح، لذلك .keys
ينتهي الوظيفة.
بعض الأفكار: يجب عليك معرفة هياكل بيانات المكتبة الصحيحة:
1 فرز الصفيف O (NLGON)، ثم تشغيل الصفيف
2 قم بإنشاء مجموعة، والبحث عن عنصر الصفيف الحالي في المجموعة وإذا لم يتم العثور عليه، وإدراج ومتابعة لجميع العناصر - O (NLGON) مرة أخرى.
كنت أفكر في حساب عدد المرات التي تظهر عنصر فريد في الصفيف. قد يكون ذلك غير فعال حقا تماما مثل الاقتراح الأصلي ولكنه كان ممتعا بالنظر إلى المشكلة. لم أفعل أي معايير على صفائف أكبر، لذلك هذا مجرد تمرين.
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]
سيعمل ذلك إذا كانت الإدخالات المكررة متتالية دائما، كما هو الحال في مثالك؛ وإلا سيكون عليك الترتيب أولا. كل_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