Bölüm 16: Kümeler ve Union-Find

C ile Veri Yapıları ve Algoritmalar · Çalışma Soruları
Ayrık kümelerDizi ile gerçeklemeAnalizAğırlıklı kuralAzaltma kuralıUygulamalarÖzet
Sercan KÜLCÜ

Çoktan Seçmeli · Ayrık Kümeler

Soru 1: Kaç Küme?

ata = [2, −1, −1, 1, 2, 3, −1, 6] dizisi kaç kümeyi gösterir ve Kume_Bul_1(5) ne döndürür?

C: Negatif değerli hücreler 1, 2 ve 6'dır: üç kök, üç küme ({1, 3, 5}, {0, 2, 4}, {6, 7}). 5'ten başlayan yol 5 → 3 → 1'dir ve ata[1] = −1 olduğu için 1 döner (2 ilerleme). A ilk ilerlemede durur; 3'ün ebeveyni olduğu için 3 kök değildir.

Çıktı Sorusu · Dizi ile Gerçekleme

Soru 2: Kök Yerine Düğüm

Aşağıdaki kod ne yazdırır? Son birleşimde amaç 0 ile 4'ün kümelerini birleştirmektir; amaç gerçekleşir mi?

int ata[6] = {-1, -1, -1, -1, -1, -1};
Birlesim_1(0, 1);
Birlesim_1(2, 3);
Birlesim_1(1, 3);
Birlesim_1(0, 4);
for (int i = 0; i < 6; i++)
  printf("%d ", Kume_Bul_1(i));
Cevabı göster

Çıktı: 4 3 3 3 4 5

  • İlk üç çağrıdan sonra 0 → 1 → 3 ve 2 → 3 bağları vardır: {0, 1, 2, 3} tek küme.
  • Birlesim_1(0, 4) yalnız ata[0] = 4 yazar. 0 kök değildir; 1'e olan bağı kopar ve 0 tek başına 4'ün altına geçer. Sonuç iki küme: {1, 2, 3} ve {0, 4}. Amaç gerçekleşmez.
012345ata403132-13-14-15

Birleşime kökler verilmelidir: Birlesim_1(Kume_Bul_1(0), Kume_Bul_1(4)); → ata[3] = 4, çıktı 4 4 4 4 4 5.

Çoktan Seçmeli · Dizi ile Gerçekleme

Soru 3: Kendine Bağlanan Kök

5 ve 7 aynı kümededir ve kümenin kökü 3'tür. Yanlışlıkla Birlesim_1(3, 3) çağrılıyor, ardından Kume_Bul_1(5). Ne olur?

A: Birlesim_1 hiçbir denetim yapmadan ata[i] = j yazar: ata[3] = 3. Kök artık negatif değil, kendini gösteriyor. Kume_Bul_1(5) 3'e varınca ata[3] >= 0 hep doğru kalır ve i = ata[i] yine 3 verir: sonsuz döngü. Bu yüzden birleşimden önce Kume_Bul ile bulunan iki kökün farklı olduğu denetlenir.

Çoktan Seçmeli · Analiz

Soru 4: Yıldız Biçimli Birleşim

n tek öğeli kümeyle başlanıyor. Birlesim_1(1, 0), Birlesim_1(2, 0), …, Birlesim_1(n−1, 0) çağrılıyor, ardından her i için Kume_Bul_1(i). Toplam kaç ilerleme (i = ata[i]) yapılır?

D: Her yeni öğe doğrudan köke (0) bağlanır; ağaç yıldızdır ve iki düzeylidir. Kume_Bul_1(0) hiç, diğer n − 1 çağrı birer ilerleme yapar (sayaçlı programda n = 8 için 7). Zincir üreten Birlesim_1(i, i+1) sırasında ise toplam n(n − 1)/2 olur. Birleşim sayısı aynıdır; maliyeti hangi ağacın hangisine bağlandığı belirler.

Çoktan Seçmeli · Ağırlıklı Kural

Soru 5: Argüman Sırası

ata = [−2, 0, −4, 2, 2, 2] iken Birlesim_2(2, 0) çağrılıyor. Dizi ne olur?

B: p = −4 + (−2) = −6. ata[2] = −4 > ata[0] = −2 yanlıştır (2 kümesinde 4, 0 kümesinde 2 düğüm), else dalı çalışır: ata[0] = 2, ata[2] = −6. Birlesim_2(0, 2) de aynı diziyi verir; kural argüman sırasından bağımsız olarak küçük ağacı büyüğe bağlar. A büyüğü küçüğe bağlar, C düğüm sayısını günlemeyi unutur.

Adım Adım Çalıştırma · Ağırlıklı Kural

Soru 6: Ağırlıklı Birleşimle Orman

n = 7, ata tümüyle −1. Sırasıyla Birlesim_2(1,2), (3,1), (0,4), (1,0), (5,6), (5,1) çağrılıyor. Son dizi nedir, en derin düğüm kaçıncı düzeydedir?

0123456ata-10-11-12-13-14-15-16

Yedi tek öğeli küme; her kök −1 tutar (bir düğüm).

0123456ata-10-2112-13-14-15-16

Birlesim_2(1, 2): −1 > −1 yanlış: eşit boyda j, i'ye bağlanır. ata[2] = 1, ata[1] = −2.

0123456ata-10-311213-14-15-16

Birlesim_2(3, 1): −1 > −2 doğru: 3'ün ağacı küçük, 1'e bağlanır. ata[1] = −3.

0123456ata-20-31121304-15-16

Birlesim_2(0, 4): Eşit boy: 4, 0'a bağlanır. İki ağaç: 1 kökünde 3, 0 kökünde 2 düğüm.

0123456ata10-51121304-15-16

Birlesim_2(1, 0): −3 > −2 yanlış: 0'ın ağacı (2 düğüm) 1'in altına girer. 4 artık 3. düzeyde.

0123456ata10-51121304-2556

Birlesim_2(5, 6): Eşit boy: 6, 5'e bağlanır; ata[5] = −2.

0123456ata10-711213041556

Birlesim_2(5, 1): −2 > −5 doğru: 5'in ağacı 1'e bağlanır, ata[1] = −7. En derin düğümler (4 ve 6) 3. düzeyde: ⌊lg 7⌋ + 1 = 3.

void Birlesim_2(int i, int j) {
  int p = ata[i] + ata[j];  /* -(toplam) */
  if (ata[i] > ata[j]) {    /* i'de az düğüm */
    ata[i] = j;
    ata[j] = p;
  } else {                  /* j'de az/eşit */
    ata[j] = i;
    ata[i] = p;
  }
}

Çoktan Seçmeli · Ağırlıklı Kural

Soru 7: Dört Düzey İçin

Yalnız Birlesim_2 ile kurulmuş bir ağacın düzey sayısı 4'tür. Ağaçta en az kaç düğüm vardır?

B: Lemmaya göre düzey sayısı ⌊lg m⌋ + 1'i aşamaz. 4 ≤ ⌊lg m⌋ + 1 için ⌊lg m⌋ ≥ 3, yani m ≥ 8. Bir düğümün düzeyi ancak ağacı eşit ya da daha büyük bir ağaca bağlanınca artar ve ağaç en az iki katına çıkar: 1 → 2 → 4 → 8. Eşit boylu ağaçları ikişer ikişer birleştiren 8 düğüm bu sınıra ulaşır.

Hata Bulma · Ağırlıklı Kural

Soru 8: Ters Karşılaştırma

Bu Birlesim_2 sürümünde iki hata var. ata = [−2, 0, −1] iken Birlesim_2(0, 2) çağrılırsa dizi ne olur, ardından Kume_Bul_1(1) ne yapar? Hataları düzeltin.

void Birlesim_2(int i, int j) {
  if (ata[i] < ata[j]) {   /* i'de az düğüm */
    ata[i] = j;
    ata[j] = ata[i] + ata[j];
  } else {
    ata[j] = i;
    ata[i] = ata[i] + ata[j];
  }
}
Cevabı göster
  • −2 < −1 doğru: iki düğümlü ağaç tek düğümlünün altına girer. Negatiflerde büyük olan daha az düğümdür; doğrusu >.
  • Toplam, ata[0] ezildikten sonra alınıyor: ata[2] = 2 + (−1) = 1. Dizi [2, 0, 1]: hiç kök yok.
021

1 → 0 → 2 → 1 çevrimi yüzünden Kume_Bul_1(1) sonsuz döngüye girer. Düzeltme: önce p = ata[i] + ata[j], karşılaştırma ata[i] > ata[j]. Doğru sürüm [−3, 0, 0] verir.

Adım Adım Çalıştırma · Azaltma Kuralı

Soru 9: Zincirde Azaltma

ata = [−6, 0, 1, 2, 3, 0] iken Kume_bul_2(4) çağrılıyor. İki döngü kaçar tur döner, dizi ne olur?

012345ata-600112233405

Birlesim_1 ile oluşmuş zincir: 4 → 3 → 2 → 1 → 0. Kume_bul_2(4) çağrıldı.

012345ata-600112233405

ata[4] = 3 ≥ 0 → r = 3.

012345ata-600112233405

ata[3] = 2 ≥ 0 → r = 2.

012345ata-600112233405

ata[2] = 1 ≥ 0 → r = 1.

012345ata-600112233405

ata[1] = 0 ≥ 0 → r = 0. ata[0] = −6 < 0: kök bulundu (4 ilerleme).

012345ata-600112230405

4 doğrudan köke bağlandı; i = s = 3.

012345ata-600112030405

3 doğrudan köke bağlandı; i = s = 2.

012345ata-600102030405

2 doğrudan köke bağlandı; i = s = 1.

012345ata-600102030405

ata[1] zaten 0: fazladan bir tur; i = 0.

012345ata-600102030405

i == r: return 0. Yol üzerindeki her düğüm artık köke bağlı; ikinci Kume_bul_2(4) tek ilerleme yapar.

int Kume_bul_2(int i) {
  int r, s;
  for (r = i; ata[r] >= 0; r = ata[r])
    ;                 /* kökü bul */
  while (i != r) {    /* yolu azalt */
    s = ata[i];
    ata[i] = r;
    i = s;
  }
  return r;
}

Çoktan Seçmeli · Azaltma Kuralı

Soru 10: Ebeveyni Saklamadan

s değişkeni kaldırılmış bu sürümle ata = [−6, 0, 1, 2, 3, 0] iken Kume_bul_2(4) çağrılırsa ne olur?

int Kume_bul_2(int i) {
  int r;
  for (r = i; ata[r] >= 0; r = ata[r])
    ;
  while (i != r) {
    ata[i] = r;
    i = ata[i];
  }
  return r;
}

C: ata[4] = 0 yazıldıktan sonra i = ata[4] 0 olur, yani r'ye eşit; döngü ilk turda biter. Dizi [−6, 0, 1, 2, 0, 0]: kök doğru bulunur, 3, 2 ve 1 eski ebeveynlerine bağlı kalır (kopmazlar) ama yolları kısalmaz. Ebeveyn, bağ değiştirilmeden önce saklanmalıdır.

Kod Yazma · Azaltma Kuralı

Soru 11: Yol Yarılama

Kume_bul_2 yolu iki geçişte kısaltır. Tek geçişte çalışan bir azaltma da yazılabilir: yoldaki düğüm, ebeveyninin ebeveynine bağlanır ve o düğüme geçilir (yol yarılama). int bul_yari(int i) yordamını yazın. ata = [−6, 0, 1, 2, 3, 0] iken bul_yari(4) sonrası dizi ne olur?

Cevabı göster
int bul_yari(int i) {
  while (ata[i] >= 0) {
    if (ata[ata[i]] >= 0)        /* büyük ata var */
      ata[i] = ata[ata[i]];
    i = ata[i];
  }
  return i;
}

i = 4: ata[4] = 2, i = 2. i = 2: ata[2] = 0, i = 0: kök, 0 döner. Dizi [−6, 0, 0, 2, 2, 0].

012345ata-600102232405

4'ün yolu 4 kenardan 2 kenara indi; atlanan 3'ün bağı değişmez. Tek döngü yeter, ek değişken gerekmez.

Çoktan Seçmeli · Özet

Soru 12: Yalnız Ağırlıklı Kural

Azaltma kuralı kullanılmıyor: birleşimler Birlesim_2, aramalar Kume_Bul_1 ile yapılıyor. n − 1 birleşim ve m küme bulmanın en kötü toplam maliyeti nedir?

A: Her birleşim O(1)'dir: toplam O(n). Ağırlıklı kural derinliği ⌊lg n⌋ + 1 ile sınırladığı için her arama O(lg n): m arama O(m lg n). B kuralsız Birlesim_1'in sınırıdır (zincir); C ancak azaltma kuralı da eklenince elde edilir.

Çıktı Sorusu · Uygulamalar

Soru 13: Bağlı mı Sorguları

ata[0..4] −1 ile başlıyor; islem[k][0] 1 ise bağla, 0 ise sor. Program ne yazdırır, sonda ata nedir?

int islem[7][3] = {{1,0,1}, {1,2,3}, {0,1,3},
  {1,1,2}, {0,0,3}, {1,3,0}, {0,4,0}};
for (int k = 0; k < 7; k++) {
  int a = Kume_bul_2(islem[k][1]);
  int b = Kume_bul_2(islem[k][2]);
  if (islem[k][0] == 0)
    printf(a == b ? "E " : "H ");
  else if (a != b)
    Birlesim_2(a, b);
  else
    printf("gereksiz ");
}
Cevabı göster

Çıktı: H E gereksiz H

  • (0,1) ve (2,3) bağlanır: ata = [−2, 0, −2, 2, −1]. Soru (1,3): kökler 0 ve 2 → H.
  • Bağla (1,2): kökler 0 ve 2, eşit boy → 2, 0'a bağlanır, ata[0] = −4.
  • Soru (0,3): Kume_bul_2(3) 3 → 2 → 0 yolunu bulur ve 3'ü köke bağlar → E.
  • Bağla (3,0): ikisinin kökü 0 → gereksiz. Soru (4,0): 4 tek başına → H.

Son dizi [−4, 0, 0, 0, −1]: {0, 1, 2, 3} ve {4}. Ağ bağlantısı sorularının çevrimiçi yanıtlanması böyle yapılır.

Adım Adım Çalıştırma · Uygulamalar

Soru 14: Döngüyü Kapatan Kenar

Yönsüz bir çizgenin kenarları sırayla işleniyor. dongu_bul(5, 6, u, v) neyi döndürür?

01234ata-10-11-12-13-14

Kenarlar sırayla: (0,1), (2,3), (1,3), (4,2), (0,2), (3,4). Her düğüm ayrı küme.

01234ata-2001-12-13-14

(0,1): kökler 0 ve 1 farklı → Birlesim_2(0, 1).

01234ata-2001-2223-14

(2,3): kökler 2 ve 3 farklı → Birlesim_2(2, 3).

01234ata-40010223-14

(1,3): kökler 0 ve 2 farklı → Birlesim_2(0, 2).

01234ata-5001022304

(4,2): kökler 4 ve 0 farklı → Birlesim_2(4, 0).

01234ata-5001022304

(0,2): iki ucun kökü de 0. Aralarında zaten yol var (0 – 1 – 3 – 2); kenar döngüyü kapatır: return 4.

int dongu_bul(int n, int m, int u[], int v[]) {
  for (int i = 0; i < n; i++) ata[i] = -1;
  for (int e = 0; e < m; e++) {
    int a = Kume_bul_2(u[e]), b = Kume_bul_2(v[e]);
    if (a == b) return e;      /* döngü */
    Birlesim_2(a, b);
  }
  return -1;                   /* orman */
}

Çoktan Seçmeli · Uygulamalar

Soru 15: Desteklenmeyen İşlem

Ebeveyn dizisi, ağırlıklı kural ve azaltma kuralıyla kurulan union-find yapısı aşağıdakilerden hangisini verimli biçimde yapamaz?

D: Yapı yalnız birleştirir: hangi düğümün hangi birleşimle geldiği saklanmaz, azaltma kuralı da ağaç biçimini değiştirir. Bir ağ bağlantısı silindiğinde yapı baştan kurulur. A iki Kume_bul ile, B kökteki −ata[kök] ile, C Birlesim_2 ile yapılır.

Kod Yazma · Özet

Soru 16: En Büyük Küme

Birlesim_2 ile kurulmuş bir ata dizisinde en çok öğeli kümenin kökünü döndüren, öğe sayısını da işaretçi parametresiyle veren int en_buyuk(const int ata[], int n, int *boy) yordamını yazın. ata = [3, −3, −5, 2, 2, 2, −1, 1, 1] için sonuç nedir?

Cevabı göster
int en_buyuk(const int ata[], int n, int *boy) {
  int kok = -1, b = 0;
  for (int i = 0; i < n; i++)
    if (ata[i] < 0 && -ata[i] > b) {   /* kök */
      b = -ata[i];
      kok = i;
    }
  if (boy != NULL) *boy = b;
  return kok;
}

Kökler 1 (3 öğe), 2 (5 öğe) ve 6 (1 öğe): yordam 2 döndürür, *boy = 5.

Öğe sayısı kökte tutulduğu için hiçbir Kume_bul çağrısı gerekmez: tek geçiş, O(n). const diziyi değiştirmeyeceğini, NULL denetimi de çağıranın boyu istemeyebileceğini gösterir.

Son

Bölüm 16: Kümeler ve Union-Find — Çalışma Soruları
Konu anlatımı: Kümeler ve union-find sunumu · Ders notu · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.