18-dars: DFS
3-darajaOltin — graflar17–24 darslar
Bu darsdan keyin siz
- DFS ni stek bilan yozasiz va nega rekursiv variant xavfli ekanini bilasiz
- grafdagi bog’langan guruhlarni sanaysiz
- har uch aynan bir marta ko’rilishini ta’minlaysiz
Avval qo’lda
“Avval qo’lda” bo'limiga havolaAvval qo‘lda
O’sha do’stlik grafini oling. Bitta uchdan boshlang va qoidaga amal qiling: hali bormagan qo’shnilardan bittasini tanlab o’sha yerga o’ting, ismini ro’yxatga yozing.
Bormagan qo’shni qolmasa — oldingi uchga qayting va u yerdan davom eting. Yurgan yo’lingizni qalam bilan qalinlashtirib boring.
Nimani sezishingiz kerak
Qalinlashtirilgan chiziqlar daraxt hosil qildi va unda halqa yo’q — chunki siz hech qachon ko’rilgan uchga qaytib kirmadingiz. Ro’yxat esa DFS tartibidir.
Kompyuter yopiq. Bu mashq tugamaguncha kod ochilmaydi.
DFS nima
“DFS nima” bo'limiga havolaDFSdepth-first search
Grafni kezish usuli: har qadamda imkon boricha chuqurroq kiriladi, yo’l tugagandagina orqaga qaytiladi.
Yozishning ikki yo’li bor va ikkalasi ham bir xil ish qiladi. Rekursiv variant qisqa, stekli variant esa xavfsiz.
qoshni = {1: [2, 3], 2: [1, 4], 3: [1], 4: [2]}korilgan = set()def dfs(u): korilgan.add(u) for v in qoshni[u]: if v not in korilgan: dfs(v)dfs(1)print(sorted(korilgan))Shuning uchun bu modulda DFS stek bilan yoziladi. Bu aynan 11-darsdagi stek — rekursiya ham ichkarida xuddi shu tuzilmani ishlatadi, faqat uni Python o’zi boshqaradi.
qoshni = {1: [2, 3], 2: [1, 4], 3: [1], 4: [2]}korilgan = {1}stek = [1]tartib = []while stek: u = stek.pop() tartib.append(u) for v in qoshni[u]: if v not in korilgan: korilgan.add(v) stek.append(v)print(tartib)Avval o'ylang: bu kod nima chiqaradi? Ishga tushirishdan oldin yozing.
qoshni = {1: [2, 3], 2: [1, 4], 3: [1], 4: [2]}korilgan = {1}stek = [1]tartib = []while stek: u = stek.pop() tartib.append(u) for v in qoshni[u]: if v not in korilgan: korilgan.add(v) stek.append(v)print(tartib)Haqiqiy natija:
[1, 3, 2, 4]
Tartib rekursiv variantnikidan farq qiladi: stekdan eng OXIRGI qo'shilgan olinadi, shuning uchun 2 dan oldin 3 ochiladi. Ikkala tartib ham to'g'ri DFS.
Javobni ko'rish
[1, 3, 2, 4]
Tartib rekursiv variantnikidan farq qiladi: stekdan eng OXIRGI qo'shilgan olinadi, shuning uchun 2 dan oldin 3 ochiladi. Ikkala tartib ham to'g'ri DFS.
Sekin yechim
“Sekin yechim” bo'limiga havolaEndi masala: grafda nechta alohida guruh bor? Birinchi fikr — har uchdan boshlab uning butun guruhini kezib chiqish.
qoshni = {1: [2], 2: [1, 3], 3: [2], 4: [], 5: [6], 6: [5]}guruhlar = set()for boshi in qoshni: korilgan = {boshi} stek = [boshi] while stek: u = stek.pop() for v in qoshni[u]: if v not in korilgan: korilgan.add(v) stek.append(v) guruhlar.add(frozenset(korilgan))print(len(guruhlar))Avval o'ylang: bu kod nima chiqaradi? Ishga tushirishdan oldin yozing.
qoshni = {1: [2], 2: [1, 3], 3: [2], 4: [], 5: [6], 6: [5]}guruhlar = set()for boshi in qoshni: korilgan = {boshi} stek = [boshi] while stek: u = stek.pop() for v in qoshni[u]: if v not in korilgan: korilgan.add(v) stek.append(v) guruhlar.add(frozenset(korilgan))print(len(guruhlar))Haqiqiy natija:
3
Guruhlar: 1-2-3, yolg'iz 4, va 5-6. Javob to'g'ri, lekin uch a'zoli guruh uch marta to'liq kezib chiqildi.
Javobni ko'rish
3
Guruhlar: 1-2-3, yolg'iz 4, va 5-6. Javob to'g'ri, lekin uch a'zoli guruh uch marta to'liq kezib chiqildi.
Nega sekin
“Nega sekin” bo'limiga havolaHar uchdan alohida kezish — guruh o’z a’zolari soniga teng marta
qayta ko’riladi. Bitta katta guruhdan iborat grafda bu n × (n + m)
ga chiqadi.
Sig‘adimi?
Bir soniyada taxminan 10⁸ oddiy amal bajariladi.
| Yechim | n = 1 000 | n = 100 000 |
|---|---|---|
Har uchdan qayta kezishn^2 | 10⁶sig‘adi | 10¹⁰sig‘maydi |
Bir marta kezishn | 1 000sig‘adi | 100 000sig‘adi |
Yashil — yechim o‘tadi. Sariq — chegarada, konstanta va til muhim bo‘lib qoladi. Qizil — bu yechim bilan bormaydi, boshqasini qidiring.
Tez yechim
“Tez yechim” bo'limiga havolaBitta o’zgarish yetadi: korilgan butun graf bo’ylab bitta marta
yuritiladi. Yangi guruh faqat hali ko’rilmagan uchdan boshlanadi.
qoshni = {1: [2], 2: [1, 3], 3: [2], 4: [], 5: [6], 6: [5]}korilgan = set()guruhlar = 0for boshi in qoshni: if boshi in korilgan: continue guruhlar += 1 korilgan.add(boshi) stek = [boshi] while stek: u = stek.pop() for v in qoshni[u]: if v not in korilgan: korilgan.add(v) stek.append(v)print(guruhlar)Avval o'ylang: bu kod nima chiqaradi? Ishga tushirishdan oldin yozing.
qoshni = {1: [2], 2: [1, 3], 3: [2], 4: [], 5: [6], 6: [5]}korilgan = set()guruhlar = 0for boshi in qoshni: if boshi in korilgan: continue guruhlar += 1 korilgan.add(boshi) stek = [boshi] while stek: u = stek.pop() for v in qoshni[u]: if v not in korilgan: korilgan.add(v) stek.append(v)print(guruhlar)Haqiqiy natija:
3
Javob o'sha, ish hajmi esa boshqa: har uch aynan bir marta stekka tushadi va har qirra bir marta ko'riladi — O(n + m).
Javobni ko'rish
3
Javob o'sha, ish hajmi esa boshqa: har uch aynan bir marta stekka tushadi va har qirra bir marta ko'riladi — O(n + m).
Sinab ko’r
“Sinab ko’r” bo'limiga havolaVizualda stekdagi uchlar sariq, tugaganlari yashil. Stekdan har doim eng oxirgi qo’shilgani olinishini kuzating — DFS shu sababli chuqurlashadi.
DFS — chuqurlikka kirish
Stekdan har doim eng oxirgi qo'shilgan uch olinadi — DFS shu sababli chuqurlashadi.
DFS stek bilan ishlaydi: eng oxirgi qo'shilgan uch birinchi bo'lib ochiladi.
Algoritmning har qadami quyidagi ro‘yxatda matn sifatida ham berilgan — vizual faqat shu qadamlarni chizadi, yangi ma’lumot qo‘shmaydi.
- A dan boshlaymiz. Stekka faqat A tushdi.
- A stekdan olindi. Endi uning qo'shnilarini ko'ramiz.
- B, D stekka qo'shildi. DFS keyingi qadamda ENG OXIRGI qo'shilganini (D) oladi — shuning uchun u chuqurlikka kiradi.
- D stekdan olindi. Endi uning qo'shnilarini ko'ramiz.
- E stekka qo'shildi. DFS keyingi qadamda ENG OXIRGI qo'shilganini (E) oladi — shuning uchun u chuqurlikka kiradi.
- E stekdan olindi. Endi uning qo'shnilarini ko'ramiz.
- F stekka qo'shildi. DFS keyingi qadamda ENG OXIRGI qo'shilganini (F) oladi — shuning uchun u chuqurlikka kiradi.
- F stekdan olindi. Endi uning qo'shnilarini ko'ramiz.
- C stekka qo'shildi. DFS keyingi qadamda ENG OXIRGI qo'shilganini (C) oladi — shuning uchun u chuqurlikka kiradi.
- C stekdan olindi. Endi uning qo'shnilarini ko'ramiz.
- C ning barcha qo'shnilari allaqachon ko'rilgan — orqaga qaytamiz.
- B stekdan olindi. Endi uning qo'shnilarini ko'ramiz.
- B ning barcha qo'shnilari allaqachon ko'rilgan — orqaga qaytamiz.
- Barcha uchlar ko'rildi: 6 qadam. Yashil qirralar — DFS yurgan yo'l; u daraxt hosil qiladi va unda sikl bo'lmaydi.
A dan boshlaymiz. Stekka faqat A tushdi.
Qadam0
Xatoni toping
“Xatoni toping” bo'limiga havolaTuzoq — xatoni toping
18 / 40
MasalaStek bilan DFS yozing va har uchni bir marta ko'ring.
Taklif qilingan yechim
Bu javobning 3-qismi noto'g'ri. Qo'shni stekka qo'shilishidan oldin «ko'rilganmi?» tekshiruvi yo'q va u o'sha zahoti belgilanmaydi. Natijada bitta uch stekka bir necha marta tushadi: siklli grafda stek shishib ketadi. To'g'risi: if v not in korilgan, so'ng darhol korilgan.add(v).
Javobning qaysi qismi noto'g'ri? Bosib belgilang.
Qo'shni stekka qo'shilishidan oldin «ko'rilganmi?» tekshiruvi yo'q va u o'sha zahoti belgilanmaydi. Natijada bitta uch stekka bir necha marta tushadi: siklli grafda stek shishib ketadi. To'g'risi: if v not in korilgan, so'ng darhol korilgan.add(v).
Ikkinchi qadamda ham nozik farq bor. Uchni stekka qo’yayotganda belgilash kerak, olayotganda emas — aks holda bir uch bir necha marta navbatga tushib ulguradi.
O’zingizni sinang
“O’zingizni sinang” bo'limiga havola1Rekursiv DFS katta grafda nega yiqiladi?
Javobni ko'rish
RecursionError: chaqiruv steki chegarasi ming atrofida, uzun zanjir
shaklidagi grafda esa chuqurlik uchlar soniga teng bo’ladi.
2Uchni qachon korilgan ga qo'shish kerak?
Javobni ko'rish
Stekka qo’yayotganda, olayotganda emas. Aks holda bir uch bir necha marta stekka tushadi.
3DFS yurgan qirralar qanday shakl hosil qiladi?
Javobni ko'rish
Daraxt: har uchga aynan bir marta kiriladi, demak halqa paydo bo’lmaydi. Bu daraxt 23-darsda alohida o’rganiladi.
4Bog'langan guruhlarni sanashda korilgan qayerda e'lon qilinadi?
Javobni ko'rish
Tashqi siklning tashqarisida, ya’ni butun graf uchun bitta. Ichkarida
e’lon qilinsa har uchdan qayta kezish boshlanadi va yechim O(n²) bo’ladi.
5DFS ning murakkabligi qanday?
Javobni ko'rish
O(n + m): har uch bir marta ochiladi va har qirra bir marta ko’riladi.
Bundan tezroq bo’lishi mumkin emas — kirishning o’zini o’qish ham shuncha.
Masalalar
“Masalalar” bo'limiga havolaMasalalar
3 ta
- Building RoadsCSES 1666o'rta
Guruhlar sonini toping. Ularni ulash uchun har guruhdan bittadan vakil yetadi.
- Cyclic ComponentsCodeforces 977Eo'rta
Guruh halqa bo‘lishining oddiy belgisi bor: undagi har uchning darajasi aynan 2.
- Flight Routes CheckCSES 1682qiyin
Bir tomonlama grafda ikki marta kezing: oddiy yo‘nalishda va barcha qirralar teskari qilingan grafda.
Lokal mashq: mashqlar/18-komponentalar/ — masala matni, testlar va tekshir.py. Avval sekin.py ni o‘zingiz yozing, keyin tayyorini oching.
Topshiriq
“Topshiriq” bo'limiga havolaTopshiriq — darajangizni tanlang
Asos · 5–7-sinf — Vizual va aniq ko'rsatmali
17-darsdagi grafingizda DFS ni qo’lda bajaring va stekda har qadamda nima turganini yozib boring. Keyin boshqa uchdan boshlab takrorlang — tartib o’zgardimi, guruhlar soni-chi?
Mustahkam · 8–9-sinf — Matnli masala, o'zingiz tuzasiz
18-komponentalar masalasini yeching. sekin.py va tez.py
vaqtini python mashqlar/tekshir.py 18-komponentalar bilan
solishtiring.
Chuqur · 10–11-sinf — Algoritmik, chegara holatlari bilan
Rekursiv DFS ni yozing va uni 100 000 uchli zanjir grafida
yurgizing (1-2, 2-3 va hokazo). Qanday xato chiqdi? Keyin
sys.setrecursionlimit() bilan chegarani oshirib ko’ring — nima
bo’ladi va nega bu yechim emas?
Darsni belgilash uchun JavaScript kerak. Bu progressni saqlash uchun ishlatiladi — darslikning o'zi JavaScript'siz ham to'liq o'qiladi.