Bölüm 13: Kümeler ve Ayrık Kümeler
Klasik Soru · Küme ADT
Soru 1: Set Metotlarıyla Küme Cebri
Programın çıktısı nedir? Kullanılan her metodun hangi küme işlemine karşılık geldiğini belirtiniz.
Set<Integer> a =
new TreeSet<>(List.of(5, 1, 9, 3, 7));
Set<Integer> b =
new HashSet<>(List.of(3, 4, 5, 6));
Set<Integer> k = new TreeSet<>(a);
k.retainAll(b);
Set<Integer> f = new TreeSet<>(a);
f.removeAll(b);
Set<Integer> u = new TreeSet<>(a);
u.addAll(b);
System.out.println(k + " " + f + " "
+ u.size() + " " + a.add(3));Cevabı göster
retainAll= kesişim A ∩ B = [3, 5]removeAll= fark A − B = [1, 7, 9]addAll= birleşim = [1, 3, 4, 5, 6, 7, 9] → boyut 7- 3 zaten a'da:
addfalsedöner, küme değişmez.
Çıktı: [3, 5] [1, 7, 9] 7 false
Üç metot da kümeyi yerinde değiştirir; a'yı korumak için önce kopya (new TreeSet<>(a)) alınmıştır. TreeSet olduğu için sonuçlar sıralı yazdırılır.
Çoktan Seçmeli · Küme ADT
Soru 2: Kopya mı, Referans mı?
Programın çıktısı nedir?
Set<Integer> a =
new TreeSet<>(List.of(1, 2, 3, 4));
Set<Integer> b =
new TreeSet<>(List.of(3, 4, 5));
Set<Integer> kes = a;
kes.retainAll(b);
System.out.println(a + " " + kes.size());Doğru: C. kes = a kopya oluşturmaz; iki değişken aynı nesneyi gösterir. retainAll yerinde çalıştığı için a da [3, 4] olur. A şıkkı a'nın kopyalandığını varsayar; kesişimi a'yı bozmadan almak için new TreeSet<>(a) gerekir.
Çoktan Seçmeli · Küme Uygulamaları
Soru 3: Gezinme Sırası
Programın çıktısı nedir?
Set<String> l = new LinkedHashSet<>(); Set<String> t = new TreeSet<>(); for (String s : List.of("muz", "elma", "kiraz", "elma")) { l.add(s); t.add(s); } System.out.println(l + " " + t);
Doğru: A. LinkedHashSet ekleme sırasını korur (hash tablosu + çift bağlı liste); TreeSet kırmızı-siyah ağaçtır ve alfabetik sırayla gezer. B yanlıştır: ikinci "elma" iki kümeye de eklenmez, çünkü küme tekrar tutmaz.
Klasik Soru · Küme Uygulamaları
Soru 4: Zincirlemeli Hash Küme
m = 7 kovalı, zincirlemeli bir hash kümesine (kova = x % 7) sırasıyla 9, 16, 5, 30, 12, 16 ekleniyor. Kovaların son durumunu yazınız ve ardından yapılan contains(23) çağrısının hangi öğelere baktığını gösteriniz.
Cevabı göster
| add | 9 | 16 | 5 | 30 | 12 | 16 |
|---|---|---|---|---|---|---|
| x % 7 | 2 | 2 | 5 | 2 | 5 | 2 |
| dönüş | true | true | true | true | true | false |
Son durum: kova 2: 9 → 16 → 30, kova 5: 5 → 12; diğer kovalar boş; n = 5. İkinci 16 için kova 2 taranır, 16 bulunur ve eklenmez: kümeyi küme yapan budur.
contains(23): 23 % 7 = 2 → yalnızca kova 2'deki 9, 16, 30 ile karşılaştırılır (3 karşılaştırma), bulunamaz → false. Diğer kovalara hiç bakılmaz; ortalama zincir boyu n/m sabit kaldıkça işlem O(1).
Klasik Soru · Küme Uygulamaları
Soru 5: TreeSet ile Sıralı Sorgular
Programın çıktısı nedir? Bu sorgular HashSet ile yapılsaydı maliyet nasıl değişirdi?
TreeSet<Integer> s = new TreeSet<>(
List.of(8, 3, 14, 21, 5, 17, 30));
System.out.println(s.first() + " "
+ s.last() + " " + s.floor(10) + " "
+ s.ceiling(17) + " " + s.subSet(5, 21));Cevabı göster
Çıktı: 3 30 8 17 [5, 8, 14, 17]
first/last: en küçük 3, en büyük 30.floor(10): ≤ 10 olan en büyük öğe → 8.ceiling(17): ≥ 17 olan en küçük öğe → 17'nin kendisi (eşitlik dahil).subSet(5, 21): alt sınır dahil, üst sınır hariç → [5, 8, 14, 17].
Dengeli ağaçta her biri O(log n) (aralık sorgusu + k çıktı). HashSet'te sıra bilgisi olmadığından aynı sorgular tüm öğeleri tarayarak O(n) sürer.
Çoktan Seçmeli · Küme Uygulamaları
Soru 6: TreeSet ve compare
Kisi sınıfında ad ve soyad alanları var. Program ne yazdırır?
TreeSet<Kisi> s = new TreeSet<>( Comparator.comparing((Kisi k) -> k.soyad)); s.add(new Kisi("Ali", "Kaya")); s.add(new Kisi("Ayşe", "Kaya")); s.add(new Kisi("Can", "Demir")); System.out.println(s.size());
Doğru: D. TreeSet eşitliği compare(a, b) == 0 ile belirler; "Ayşe Kaya", "Ali Kaya" ile aynı sayılır ve eklenmez (add false). A şıkkı equals'a bakıldığını varsayar. Düzeltme: .thenComparing(k -> k.ad), yani sıralama equals ile tutarlı olmalı. Comparator verildiği için Comparable gerekmez (C yanlış).
Adım Adım Çalıştırma · Küme Uygulamaları
Soru 7: İki İmleçle Birleşim ve Kesişim
Sıralı a = {2, 4, 6, 8} ve b = {1, 4, 5, 8, 9} dizileri için metot bittiğinde bir ve kes listeleri ne olur?
İki imleç dizilerin başında. Birleştirme sıralamasındaki merge gibi tek geçiş yapılacak.
a[0] = 2 > b[0] = 1 → 1 yalnızca b'de: birleşime eklenir, j++.
a[0] = 2 < b[1] = 4 → 2 yalnızca a'da: birleşime eklenir, i++.
a[1] = b[1] = 4 → ortak öğe: birleşime bir kez, kesişime de eklenir; iki imleç ilerler.
a[2] = 6 > b[2] = 5 → 5 birleşime eklenir, j++.
a[2] = 6 < b[3] = 8 → 6 birleşime eklenir, i++.
a[3] = b[3] = 8 → ortak öğe; i = 4, j = 4.
i = 4 = a.length → ana döngü biter; a'da kalan yok. Kesişim tamam: artık ortak öğe olamaz.
b'de kalan 9 doğrudan birleşime eklenir.
Sonuç: bir = [1, 2, 4, 5, 6, 8, 9], kes = [4, 8]. Her adımda en az bir imleç ilerler → O(n + m).
void birlesimKesisim(int[] a, int[] b, List<Integer> bir, List<Integer> kes) { int i = 0, j = 0; while (i < a.length && j < b.length) { if (a[i] < b[j]) bir.add(a[i++]); else if (a[i] > b[j]) bir.add(b[j++]); else { // ortak öğe bir.add(a[i]); kes.add(a[i]); i++; j++; } } while (i < a.length) bir.add(a[i++]); while (j < b.length) bir.add(b[j++]); }
Çoktan Seçmeli · Küme Uygulamaları
Soru 8: Hash mi, Ağaç mı?
Aşağıdaki işlemlerden hangisinde hash tabanlı küme, dengeli BST tabanlı kümeden asimptotik olarak daha yavaştır?
Doğru: B. Hash tablosu sıra bilgisi tutmaz; ≤ x olan en büyük öğeyi bulmak için tüm öğeler taranır: O(n). Dengeli BST'de O(log n). contains/add hash'te O(1)*, BST'de O(log n). D çekicidir ama hash ile birleşim O(n + m)*, BST ile O(m log(n + m)): hash yine daha hızlıdır.
Klasik Soru · Küme Uygulamaları
Soru 9: Bit Vektörüyle Küme Cebri
Program ne yazdırır? Yazdırılan her sayının temsil ettiği kümeyi de belirtiniz.
int a = 0, b = 0; for (int x : new int[]{0, 2, 5, 6}) a |= 1 << x; for (int x : new int[]{2, 3, 6}) b |= 1 << x; System.out.println((a | b) + " " + (a & b) + " " + (a & ~b) + " " + Integer.bitCount(a));
Cevabı göster
a = 0110 0101₂ = 101 ({0, 2, 5, 6}), b = 0100 1100₂ = 76 ({2, 3, 6}).
| İfade | İşlem | Bitler | Değer | Küme |
|---|---|---|---|---|
| a | b | birleşim | 0110 1101 | 109 | {0, 2, 3, 5, 6} |
| a & b | kesişim | 0100 0100 | 68 | {2, 6} |
| a & ~b | fark a − b | 0010 0001 | 33 | {0, 5} |
| bitCount(a) | |a| | 4 tane 1 | 4 | — |
Çıktı: 109 68 33 4. Evren 0..31 içinde kaldığı için her işlem tek makine komutu, O(1); daha büyük evrende java.util.BitSet ile O(U / 64).
Klasik Soru · Çoklu Küme
Soru 10: Çoklu Küme Sayaçları
Sunumdaki CokluKume sınıfı (öğe → sayaç eşlemesi; sayaç 1 iken remove girdiyi siler) kullanılıyor. Program ne yazdırır, eşlemenin son hâli nedir?
CokluKume<Character> m = new CokluKume<>(); for (char c : "ananas".toCharArray()) m.add(c); m.remove('a'); m.remove('s'); System.out.println(m.count('a') + " " + m.count('s') + " " + m.size() + " " + m.remove('x'));
Cevabı göster
- Eklemelerden sonra {a:3, n:2, s:1}, size = 6 (tekrarlar dahil).
remove('a'): sayaç 3 → 2, size = 5.remove('s'): sayaç 1 olduğundan girdi tamamen silinir (0 sayaç saklanmaz), size = 4.remove('x'):say.getnull →false, size değişmez.
Çıktı: 2 0 4 false; eşleme {a:2, n:2}. count('s'), getOrDefault sayesinde 0 döner.
Aynı harfler bir HashSet'e eklenseydi küme {a, n, s} olurdu (boyut 3): küme yalnızca "var mı?" bilgisini, çoklu küme ise çokluğu tutar. Tüm işlemler tek hash erişimi, ortalama O(1).
Çoktan Seçmeli · Çoklu Küme
Soru 11: Sıfır Sayaçlı Girdiler
remove metodunda if (c == 1) say.remove(e); else say.put(e, c - 1); yerine her zaman say.put(e, c - 1); yazılırsa ne olur?
boolean remove(E e) { Integer c = say.get(e); if (c == null) return false; say.put(e, c - 1); // değişiklik n--; return true; }
Doğru: C. Sayaç 0 olunca girdi silinmezse c == null kontrolü artık "öğe yok" durumunu yakalayamaz: ikinci remove sayaç −1 yapar ve size'ı bozar; ayrıca farklı öğe sayısı (say.size()) şişer. A çekicidir: ilk remove'dan sonra count gerçekten 0 döner, hata ancak sonraki işlemlerde ortaya çıkar.
Çoktan Seçmeli · Ayrık Kümeler (Union-Find)
Soru 12: Naif Birleştirmede Zincir
Naif union(x, y) her zaman kök(x)'i kök(y)'nin altına bağlıyor (boyut/rütbe yok, yol kısaltma yok). 5 tekil kümede sırasıyla union(1,2), union(2,3), union(3,4), union(4,5) yapılırsa ağacın yüksekliği ve find(1)'in izlediği bağlantı sayısı ne olur?
Doğru: D. Her adımda büyüyen ağacın kökü tekil düğümün altına girer: 1 → 2 → 3 → 4 → 5 zinciri, yükseklik n − 1 = 4; find(1) dört bağlantı izler, en kötü O(n). A, boyuta göre birleştirmenin sonucudur: orada tekil düğümler büyük ağacın köküne eklenir ve ağaç yıldız (yükseklik 1) olur.
Klasik Soru · Ayrık Kümeler (Union-Find)
Soru 13: Boyuta Göre Birleştirme
A–H harfleri bir hash tablosuyla 0–7 indislerine eşlenmiş; boyuta göre birleştirme (küçük ağacın kökü büyüğün köküne; boyutlar eşitse ikinci kök birincinin altına) yapılıyor, yol kısaltma yok. Aşağıdaki işlemlerden sonra her harfin ebeveyni ve ağacın yüksekliği nedir?
union(A,B), union(C,D), union(E,F), union(C,G), union(A,C), union(E,H), union(E,B)
Cevabı göster
- (A,B), (C,D), (E,F): eşit boyut → B → A, D → C, F → E.
- (C,G): 2 > 1 → G → C (boyut C = 3).
- (A,C): boyut 2 < 3 → A → C (boyut C = 5).
- (E,H): 2 > 1 → H → E (boyut E = 3).
- (E,B): find(B) = C (B → A → C); 3 < 5 → E → C (boyut C = 8).
| A | B | C | D | E | F | G | H | |
|---|---|---|---|---|---|---|---|---|
| ebeveyn | C | A | C | C | C | E | C | E |
Tek küme, kök C; yükseklik 2 (B, F, H derinlik 2). Boyuta göre birleştirme yüksekliği ≤ log₂ 8 = 3 tutar.
Adım Adım Çalıştırma · Ayrık Kümeler (Union-Find)
Soru 14: Rütbe ve Yol Kısaltma
6 öğeli yapıda (ebeveyn[i] = i, sira[i] = 0) koddaki beş union çağrısından sonra ebeveyn ve sira dizileri ne olur?
union(0,1): kökler 0 ve 1, rütbeler eşit (0) → ebeveyn[0] = 1, sira[1] = 1.
union(2,3): eşit rütbe → ebeveyn[2] = 3, sira[3] = 1.
union(4,5): eşit rütbe → ebeveyn[4] = 5, sira[5] = 1.
union(1,3): 1 ve 3 zaten kök, rütbeler eşit (1) → ebeveyn[1] = 3, sira[3] = 2.
union(4,0): find(4): ebeveyn[4] = 5 ≠ 4 → find(5); 5 kök. kx = 5 (ebeveyn[4] zaten 5).
find(0): 0 → 1 → 3 yolu boyunca özyineleme; 3 köktür.
Dönüşte yol kısaltma: ebeveyn[0] = 1 → 3. 0 artık doğrudan köke bağlı; ky = 3.
sira[5] = 1 < sira[3] = 2 → alçak ağacın kökü 5, 3'e bağlanır: ebeveyn[5] = 3. Rütbe değişmez.
Sonuç: ebeveyn = [3, 3, 3, 3, 5, 3], sira = [0, 1, 0, 2, 0, 1]; tek küme, kök 3. 4 hâlâ 5 üzerinden bağlı; yolu ilk find(4)'te kısalır.
int find(int x) { if (ebeveyn[x] != x) ebeveyn[x] = find(ebeveyn[x]); return ebeveyn[x]; } boolean union(int x, int y) { int kx = find(x), ky = find(y); if (kx == ky) return false; if (sira[kx] < sira[ky]) ebeveyn[kx] = ky; else if (sira[kx] > sira[ky]) ebeveyn[ky] = kx; else { ebeveyn[kx] = ky; sira[ky]++; } return true; } // union(0,1); union(2,3); union(4,5); // union(1,3); union(4,0);
Çoktan Seçmeli · Ayrık Kümeler (Union-Find)
Soru 15: Union-Find Karmaşıklığı
n öğe üzerinde m tane find/union işlemi, rütbeye göre birleştirme ve yol kısaltma birlikte kullanılarak yapılıyor. Toplam maliyet nedir?
Doğru: C (Tarjan, 1975). α(n) son derece yavaş büyür; evrendeki atom sayısı kadar öğe için bile ≤ 4, yani pratikte sabittir. A çekicidir ama bu sınır yalnızca rütbe/boyuta göre birleştirme (ya da yalnızca yol kısaltma) içindir. D yanlıştır: sınır amortizedir, tek bir find yine O(log n) sürebilir. Bellek: ebeveyn ve sira dizileri, O(n).
Klasik Soru · Ayrık Kümeler (Union-Find)
Soru 16: Bağlantılı Bileşen Sayısı
bilesenSay(8, new int[][]{{0,1}, {2,3}, {1,2}, {0,3}, {4,5}, {5,4}}) ne döndürür? Hangi kenarlarda union false döner ve bu ne anlama gelir?
int bilesenSay(int n, int[][] kenar) { UnionFind uf = new UnionFind(n); int say = n; // n tekil küme for (int[] e : kenar) if (uf.union(e[0], e[1])) say--; return say; }
Cevabı göster
| Kenar | (0,1) | (2,3) | (1,2) | (0,3) | (4,5) | (5,4) |
|---|---|---|---|---|---|---|
| union | true | true | true | false | true | false |
| say | 7 | 6 | 5 | 5 | 4 | 4 |
Dönüş: 4; bileşenler {0, 1, 2, 3}, {4, 5}, {6}, {7}.
(0,3): 0 ve 3 zaten (1,2) üzerinden bağlı, kenar bir döngü kapatır. (5,4): aynı kenarın tekrarı. İkisinde de say değişmez.
Toplam O(n + m·α(n)); kenarlar sonradan geldikçe sayaç anında güncellenir (dinamik bağlılık).
Çoktan Seçmeli · Özet
Soru 17: Kümedeki Öğeyi Değiştirmek
Nokta sınıfının equals ve hashCode metotları x ve y alanlarını kullanıyor. Program ne yazdırır?
Set<Nokta> s = new HashSet<>(); Nokta p = new Nokta(1, 2); s.add(p); p.x = 5; System.out.println(s.contains(p) + " " + s.size());
Doğru: A. p, (1, 2) hash'inin kovasına yerleşti. x değişince hash kodu da değişir; contains(p) yeni hash'in kovasına bakar ve p'yi bulamaz. Öğe ise eski kovada durur, size 1 kalır. B çekicidir ("aynı nesne"), ama HashSet nesneyi kova üzerinden arar. Kümedeki nesnelerin hash'e giren alanları değiştirilmemelidir.