Bölüm 15: Çizgeler
Çoktan Seçmeli · Giriş
Soru 1: Königsberg'e Köprü Eklemek
Königsberg çizgesinde dereceler A: 5, B: 3, C: 3, D: 3'tür. Her köprüden tam bir kez geçip başlanan yere dönen bir yürüyüşün mümkün olması için en az kaç yeni köprü yapılmalıdır?
B: Böyle bir yürüyüş için her derece çift olmalıdır. Yeni bir köprü iki ucun derecesini birer artırır; tek dereceli dört düğüm ikişer ikişer eşlenirse (ör. A–B ve C–D) 2 köprü yeter. Tek köprü yalnızca iki düğümü düzeltir; iki tek dereceli düğüm kalır.
Çoktan Seçmeli · Tanımlar ve Terminoloji
Soru 2: Derecelerden Kenar Sayısı
Düğüm dereceleri 4, 3, 3, 2, 2, 2 olan basit, yönsüz bir çizgenin kaç kenarı vardır?
D: Teorem 1'e göre derecelerin toplamı 2e'dir: 4 + 3 + 3 + 2 + 2 + 2 = 16 → e = 8. Her kenar iki ucunun derecesine birer katkı yapar; 16 kenar sayısı değil, uç sayısıdır. Böyle bir çizge gerçekten kurulabilir (toplam çift ve en büyük derece 4 < 6).
Klasik Soru · Gerçeklemeler
Soru 3: Yönlü Çizgenin Matrisi
Yönlü çizgenin kenarları <0,1>, <0,2>, <1,2>, <2,0>, <2,3>, <3,1>. Komşuluk matrisini yazın; her düğümün giriş ve çıkış derecesini bulun. Sunumdaki derece(a, 4, 2) ne döndürür? Matris simetrik midir?
Cevabı göster
| kuyruk \ baş | 0 | 1 | 2 | 3 | çıkış |
|---|---|---|---|---|---|
| 0 | 0 | 1 | 1 | 0 | 2 |
| 1 | 0 | 0 | 1 | 0 | 1 |
| 2 | 1 | 0 | 0 | 1 | 2 |
| 3 | 0 | 1 | 0 | 0 | 1 |
| giriş | 1 | 2 | 2 | 1 |
Satır toplamı çıkış, sütun toplamı giriş derecesidir. derece(a, 4, 2) = 2 + 2 = 4; iki döngü n'er kez döner: O(n).
Simetrik değildir: a[0][1] = 1 ama a[1][0] = 0. Yalnız <0,2> ve <2,0> çifti simetrik görünür. Çıkış dereceleri toplamı = giriş dereceleri toplamı = e = 6.
Çıktı Sorusu · Gerçeklemeler
Soru 4: Başa Ekleme ve DFS Sırası
Boş listelere kenarlar (0,1), (0,2), (1,3), (2,3) sırasıyla, her biri için kenar_ekle(u, v); kenar_ekle(v, u); yapılarak ekleniyor. Listeler nasıl görünür, sunumdaki dfs(0) ne yazdırır? Program sonunda kaç free gerekir?
void kenar_ekle(int u, int v) { /* u -> v */ oge_gost p = malloc(sizeof(oge)); if (p == NULL) exit(1); p->dugum = v; p->bag = baslik[u]; /* başa ekle */ baslik[u] = p; }
Cevabı göster
- Yeni öğe hep listenin başına girer, liste geliş sırasının tersidir:
| baslik[0] | 2 → 1 |
| baslik[1] | 3 → 0 |
| baslik[2] | 3 → 0 |
| baslik[3] | 2 → 1 |
dfs(0): 0'ın listesinde önce 2 → 2'nin listesinde 3 → 3'ün listesinde 2 (işaretli), 1 → 0 2 3 1. Listeler sıralı olsaydı 0 1 3 2 yazılırdı.
Her kenar 2 öğe: 8 malloc, sonunda her listede gezinerek 8 free.
Çoktan Seçmeli · Gerçeklemeler
Soru 5: Sıralı Dizi Gösterimi
Yönsüz bir çizge dizi ile sıralı gösterimde aşağıdaki gibi tutuluyor. komsulari_yaz(dugum, 3) ne yazdırır?
int dugum[] = {6, 8, 11, 13, 15, 16, 1, 2, 0, 2, 3, 0, 1, 1, 4, 3}; void komsulari_yaz(const int dugum[], int i) { for (int k = dugum[i]; k < dugum[i + 1]; k++) printf("%d ", dugum[k]); }
C: n = 5, e = 5 → dizi n + 2e + 1 = 16 hücre. 3'ün komşuları dugum[dugum[3]] … dugum[dugum[4] − 1], yani indis 13 ve 14: 1 ve 4. Derece de dugum[4] − dugum[3] = 2 olarak O(1)'de okunur.
Çoktan Seçmeli · Gerçeklemeler
Soru 6: Çok Bağlı Liste ile Komşuluk Listesi
e kenarlı yönsüz bir çizge için komşuluk listesi ile çok bağlı liste karşılaştırılıyor (başlık dizileri hariç). Hangisi doğrudur?
A: Komşuluk listesinde her kenar iki ucun listesinde birer öğedir (her öğede bir bağ). Çok bağlı listede kenar tek öğedir ama iki listeye birden girer: list1 ve list2. Bağ sayısı aynı kalır; kazanç, düğüm numaralarının ve kenar verisinin (ağırlık, işaret) iki kez tutulmamasıdır.
Hata Bulma · Gerçeklemeler
Soru 7: Sonsuz İçin INT_MAX
Ağırlıklı çizgede “kenar yok / henüz ulaşılmadı” için INT_MAX kullanılmıştır. Kod ne yapar? Hata nedir, nasıl giderilir?
#include <limits.h> #define SONSUZ INT_MAX int d[3] = {0, SONSUZ, SONSUZ}; int w = 5; /* (1,2) kenarı */ if (d[1] + w < d[2]) /* daha kısa mı? */ d[2] = d[1] + w; printf("%d\n", d[2]);
Cevabı göster
d[1] + w= INT_MAX + 5: işaretli tamsayı taşması, C'de tanımsız davranıştır.-fsanitize=undefinedile derlenince program “signed integer overflow” iletisiyle durur.- Taşmanın sarmaladığı (wraparound) bir derlemede toplam −2147483644 olur; INT_MAX'tan küçük olduğu için d[2]'ye yazılır ve ekrana −2147483644 çıkar: ulaşılamayan düğüm en yakın görünür.
Çözüm: sunumdaki gibi toplamı taşırmayacak büyük bir sabit (#define SONSUZ 1000000) seçmek ya da toplamadan önce sınamak:
if (d[1] != SONSUZ && d[1] + w < d[2])
d[2] = d[1] + w;Klasik Soru · Gerçeklemeler
Soru 8: Matris mi, Liste mi?
10.000 düğümlü, 20.000 kenarlı yönsüz bir yol ağı tutulacak. 64 bit sistemde int 4, işaretçi 8, sizeof(oge) 16 bayttır. int komşuluk matrisi ve komşuluk listesi yaklaşık ne kadar bellek ister? DFS ikisinde ne kadar sürer?
Cevabı göster
- Matris: 10.000 × 10.000 = 108 hücre × 4 bayt = 400 MB (char ile 100 MB, bit ile 12,5 MB). Hücrelerin %99,96'sı 0'dır.
- Liste: 10.000 başlık × 8 = 80 KB; 2e = 40.000 öğe × 16 = 640 KB → yaklaşık 720 KB.
- DFS: matriste her düğümün satırı taranır: n² = 108 adım; listede n + 2e = 50.000 adım.
Seyrek çizgede (e ≪ n²) liste hem bellekte hem gezinmede kazanır. Matris, “(u, v) kenarı var mı?” sorusunu O(1)'de yanıtlamak gerektiğinde ve çizge yoğunken seçilir.
Adım Adım Çalıştırma · Gezinmeler
Soru 9: dfs(0) Adım Adım
Şekildeki çizge sıralı komşuluk listeleriyle tutuluyor. dfs(0) düğümleri hangi sırayla yazdırır? Çağrı yığını nasıl değişir? (Kalın kenarlar DFS ağacıdır.)
Listeler (sıralı): 0: 1 4 · 1: 0 2 3 · 2: 1 5 · 3: 1 5 · 4: 0 6 · 5: 2 3 6 · 6: 4 5.
0 işaretlenir, yazılır. Listesinde ilk öğe 1.
1 ziyaret edilmemiş → dfs(1).
1'in listesinde 0 işaretli, atlanır; 2 → dfs(2).
2'nin listesi: 1 işaretli, 5 → dfs(5).
5'in listesi: 2 işaretli, 3 → dfs(3). 3'ün komşuları 1 ve 5 işaretli: hemen döner.
5'te kalınan yerden: sıradaki komşu 6 → dfs(6).
6'nın listesi: 4 → dfs(4). Yığın en derin hâlinde: 6 çerçeve.
4'ün komşuları işaretli; çerçeveler sırayla kapanır, 1'de 3 ve 0'da 4 atlanır. Sıra 0 1 2 5 3 6 4; 6 ağaç kenarı = n − 1. O(n + e).
void dfs(int v) { oge_gost w; ugranildi[v] = TRUE; printf("%d ", v); for (w = baslik[v]; w; w = w->bag) if (!ugranildi[w->dugum]) dfs(w->dugum); }
Adım Adım Çalıştırma · Gezinmeler
Soru 10: bfs(0) Adım Adım
Aynı çizgede bfs(0) çalışıyor. Her turda kuyruktan hangi düğüm alınır, hangileri eklenir, çıktı nedir? (Yeşil: kuyrukta.)
0 yazılır, işaretlenir, kuyruğa girer.
0 alınır; komşuları 1 ve 4 yazılıp kuyruğa eklenir ve işaretlenir.
1 alınır: 0 işaretli; 2 ve 3 eklenir.
4 alınır: 0 işaretli; 6 eklenir.
2 alınır: 1 işaretli; 5 eklenir. Bütün düğümler işaretlendi.
3 alınır: 1 ve 5 işaretli, ekleme yok.
6 alınır: 4 ve 5 işaretli.
5 alınır, ekleme yok; kuyruk boşaldı. Düzeyler: 0 | 1 4 | 2 3 6 | 5. Kalın kenarlar (BFS ağacı) en az kenarlı yolları verir.
printf("%d ", v); ugranildi[v] = TRUE;
kuyruk_ekle(&on, &arka, v);
while (on) {
v = kuyruk_al(&on, &arka);
for (w = baslik[v]; w; w = w->bag)
if (!ugranildi[w->dugum]) {
printf("%d ", w->dugum);
kuyruk_ekle(&on, &arka, w->dugum);
ugranildi[w->dugum] = TRUE;
}
}Çoktan Seçmeli · Gezinmeler
Soru 11: BFS'nin Taradığı Öğeler
1000 düğümlü, 3000 kenarlı, bağlantılı yönsüz bir çizge komşuluk listeleriyle tutuluyor. bfs(0) boyunca listelerde toplam kaç öğe taranır?
D: Her düğüm kuyruktan bir kez alınır ve listesi bir kez taranır; listelerdeki öğe sayısı derecelerin toplamı, yani 2e = 6000'dir. Toplam iş O(n + e). 1.000.000 (n²) matrisle yapılan gezinmenin maliyetidir.
Çoktan Seçmeli · Gezinmeler
Soru 12: arka NULL Yapılmazsa
BFS'nin kuyruğunda kuyruk_al yordamından if (*on == NULL) *arka = NULL; satırı silinirse ne olur?
B: Kuyruk boşalınca arka az önce free edilen düğümü gösterir (sarkan işaretçi). Ama kuyruk_ekle önce *on'a bakar; on NULL ise (*arka)->bag'e hiç dokunmaz, *on ve *arka'yı yeni düğüme bağlar. Örnek çizgede çıktı aynıdır. Yine de satır silinmemelidir: arka'yı okuyan başka bir yordam tanımsız davranışa düşer.
Çıktı Sorusu · Bileşenler ve Ötesi
Soru 13: bitisik_bilesenler Çıktısı
9 düğümlü yönsüz çizgenin kenarları (0,3), (1,4), (3,6), (2,5), (4,7), (6,8); listeler sıralı ve ugranildi[] başta FALSE. bitisik_bilesenler(9) ne yazdırır? if'e kaç kez girilir? Toplam iş neden O(n · (n + e)) değildir?
void bitisik_bilesenler(int n) { int i; for (i = 0; i < n; i++) if (!ugranildi[i]) { dfs(i); printf("\n"); } }
Cevabı göster
0 3 6 8 1 4 7 2 5
- i = 0: dfs(0) → 0, 3, 6, 8 işaretlenir. i = 1: dfs(1) → 1, 4, 7. i = 2: dfs(2) → 2, 5. Kalan i'lerde düğüm işaretli: if'e 3 kez girilir → 3 bileşen.
- Her DFS yalnızca kendi bileşenini gezer; bileşenler ayrık olduğu için her düğüm ve her liste toplamda bir kez işlenir. Dış döngünün n turu ayrıca O(n) ekler: O(n + e).
Kod Yazma · Bileşenler ve Ötesi
Soru 14: Bileşen Etiketlemek
Her düğüme bileşen numarası veren int bilesen_say(int n) yordamını yazın: etiket[v], v'nin bileşeninin numarası (0, 1, 2 …) olsun ve yordam bileşen sayısını döndürsün. Böylece “u ile v arasında yol var mı?” sorusu O(1)'de yanıtlanabilsin. Global ugranildi[] kullanmayın.
Cevabı göster
int etiket[MAX_DUGUM]; /* -1: henüz yok */ void etiketle(int v, int no) { etiket[v] = no; for (oge_gost w = baslik[v]; w; w = w->bag) if (etiket[w->dugum] < 0) etiketle(w->dugum, no); } int bilesen_say(int n) { int i, say = 0; for (i = 0; i < n; i++) etiket[i] = -1; for (i = 0; i < n; i++) if (etiket[i] < 0) etiketle(i, say++); return say; }
etiket[] hem işaret dizisi hem sonuçtur; her çağrıda baştan −1 yapıldığı için yordam tekrar tekrar çağrılabilir. Sorgu: etiket[u] == etiket[v]. Önceki sorunun çizgesinde 3 döner, etiketler 0 1 2 0 1 2 0 1 0 olur. O(n + e).
Çoktan Seçmeli · Bileşenler ve Ötesi
Soru 15: Kenar Sayısı ve Bağlantılılık
10 düğümlü basit, yönsüz bir çizge için hangisi doğrudur?
A: Bağlantılı çizgede e ≥ n − 1 = 9 olmalıdır; ama 9 kenar, örneğin 9 düğümlük bir döngü (9 kenar) ile tek başına kalan bir düğüm biçiminde de dizilebilir. 8 kenarla en az 2 bileşen kalır. Yönsüz basit çizgede en çok n(n − 1)/2 = 45 kenar olur; 90 yönlü çizgenin sınırıdır.
Adım Adım Çalıştırma · Bileşenler ve Ötesi
Soru 16: Döngü Tespiti Adım Adım
Şekildeki çizgede listeler sıralıdır (0: 1 2 · 1: 0 3 · 2: 0 4 · 3: 1 5 · 4: 2 5 · 5: 3 4). dongu_var(0, -1) hangi çağrıları yapar, döngüyü hangi kenarda yakalar?
0 işaretlenir; ilk komşu 1.
1 işaretsiz → dongu_var(1, 0).
1'in listesinde 0 işaretli ama 1'in atası → döngü sayılmaz. 3 → dongu_var(3, 1).
3'te 1 ata, atlanır; 5 → dongu_var(5, 3).
5'te 3 ata; 4 → dongu_var(4, 5).
4'ün listesi: 2 işaretsiz → dongu_var(2, 4). Yığında 6 çerçeve var.
2'nin ilk komşusu 0: işaretli ve ata (4) değil → geri kenar (2, 0): döngü 0–1–3–5–4–2–0.
TRUE her çerçevede return TRUE ile yukarı taşınır; kalan komşulara bakılmaz. En çok O(n + e).
bool dongu_var(int v, int ata) { oge_gost w; ugranildi[v] = TRUE; for (w = baslik[v]; w; w = w->bag) if (!ugranildi[w->dugum]) { if (dongu_var(w->dugum, v)) return TRUE; } else if (w->dugum != ata) return TRUE; /* geri kenar */ return FALSE; }
Çoktan Seçmeli · Bileşenler ve Ötesi
Soru 17: İki Parçalı Çizge
Aşağıdaki çizgelerden hangisi iki parçalıdır (düğümleri, her kenar farklı kümeleri bağlayacak biçimde iki kümeye ayrılabilir)?
C: Çizge iki parçalıdır ⇔ tek uzunlukta döngüsü yoktur. Altı düğümlü döngüde düğümler sırayla iki renge boyanır (0, 1, 0, 1, 0, 1) ve hiçbir kenarın iki ucu aynı renkte olmaz. Üçgen ve beşli döngü tek uzunlukludur; üçgene yaprak eklemek üçgeni ortadan kaldırmaz. Önceki sorudaki altı düğümlü çizge de bu yüzden iki parçalıdır.