Daraxtni aylanish (DFS, BFS)
Xaritadagi barcha shaharlarni aylanishni tasavvur qiling. Ikki xil strategiya bor: bir yo'nalishda imkon qadar chuqur borib, keyin orqaga qaytish — yoki avval yaqin atrofdagilarni ko'rib, keyin uzoqroqqa chiqish. Daraxtda ham aynan shu ikki yondashuv ishlaydi: DFS (chuqurlik bo'yicha) va BFS (kenglik bo'yicha).
Traversal — daraxtdagi har tugunni aynan bir marta ko'rib chiqish. Maqsad: qidirish, qiymatlarni to'plash, daraxtni qayta ishlash yoki tekshirish.
DFS — Chuqurlik bo'yicha qidiruv
DFS (Depth-First Search) — bir shoxga to imkon qadar chuqur tushib, keyin orqaga qaytib, boshqa shoxni ko'rib chiqish. Rekursiya yoki stek yordamida amalga oshiriladi.
Binar daraxtda DFS uchun uchta klassik tartib mavjud: preorder, inorder, postorder.
Preorder: ildiz → chap → o'ng
Avval ildiz, keyin chap kenja daraxt, keyin o'ng kenja daraxt. Daraxtni "yuqoridan pastga" o'qiladi.
FUNCTION PREORDER(node)
IF node = NULL
RETURN
VISIT(node) // ildiz avval
PREORDER(node.left) // chap kenja daraxt
PREORDER(node.right) // o'ng kenja daraxt
Yuqoridagi daraxt uchun: 1 → 2 → 4 → 5 → 3
Nima uchun foydali: daraxtni nusxalash, fayl tizimini ro'yxatga olish (ota-papka avval yozilishi kerak), serialization.
Inorder: chap → ildiz → o'ng
Avval chap kenja daraxt, keyin ildiz, keyin o'ng kenja daraxt.
FUNCTION INORDER(node)
IF node = NULL
RETURN
INORDER(node.left) // chap kenja daraxt
VISIT(node) // ildiz
INORDER(node.right) // o'ng kenja daraxt
Yuqoridagi daraxt uchun: 4 → 2 → 5 → 1 → 3
BST'da inorder traversal barcha qiymatlarni o'sish tartibida beradi. Bu BST'ning asosiy foydali xususiyati.
Postorder: chap → o'ng → ildiz
Avval chap kenja daraxt, keyin o'ng kenja daraxt, oxirida ildiz.
FUNCTION POSTORDER(node)
IF node = NULL
RETURN
POSTORDER(node.left) // chap kenja daraxt
POSTORDER(node.right) // o'ng kenja daraxt
VISIT(node) // ildiz oxirida
Yuqoridagi daraxt uchun: 4 → 5 → 2 → 3 → 1
Nima uchun foydali: daraxtni o'chirish (bolalar avval o'chirilishi kerak), matematik ifodalarda quyi ifodalar avval hisoblanadi, fayl hajmlarini hisoblash (papka hajmi barcha bolalar hajmlaridan keyin).
Bosqichma-bosqich dry run
Uchala DFS tartibini kuzatamiz. Rekursion chaqiruvlarni stack orqali kuzatish:
Preorder (ildiz avval):
PREORDER(A):
VISIT A → "A"
PREORDER(B):
VISIT B → "A, B"
PREORDER(D):
VISIT D → "A, B, D"
PREORDER(NULL) → qayt
PREORDER(NULL) → qayt
PREORDER(E):
VISIT E → "A, B, D, E"
PREORDER(NULL) → qayt
PREORDER(NULL) → qayt
PREORDER(C):
VISIT C → "A, B, D, E, C"
PREORDER(NULL) → qayt
PREORDER(NULL) → qayt
Preorder natija: A, B, D, E, C
Inorder natija: D, B, E, A, C
Postorder natija: D, E, B, C, A
DFS'ni iterativ amalga oshirish
Rekursiv DFS chaqiruv stacki ishlatadi. Katta daraxlarda stack overflow bo'lishi mumkin. Iterativ variant o'z stekimizni boshqaradi.
Iterativ preorder:
FUNCTION ITERATIVE_PREORDER(root)
IF root = NULL
RETURN
stack = [root]
result = []
WHILE stack bo'sh emas
node = stack dan POP
result ga node.value qo'sh
IF node.right ≠ NULL
stack ga node.right PUSH // o'ng avval → chapni avval ko'rish uchun
IF node.left ≠ NULL
stack ga node.left PUSH // chap keyin → avval chiqadi
RETURN result
Nima uchun o'ng avval push qilinadi? Stek LIFO — keyingi POP eng yuqoridagini oladi. Chapni avval ko'rishimiz uchun chap oxirida push qilinadi.
BFS — Kenglik bo'yicha qidiruv
BFS (Breadth-First Search) — daraxtni qavatma-qavat o'qiydi: birinchi ildiz, keyin birinchi qavat, keyin ikkinchi qavat va hokazo. Navbat (queue) ishlatiladi.
BFS tartibi: 1, 2, 3, 4, 5, 6
FUNCTION BFS(root)
IF root = NULL
RETURN
queue = [root]
result = []
WHILE queue bo'sh emas
node = queue dan DEQUEUE (old)
result ga node.value qo'sh
IF node.left ≠ NULL
queue ga node.left ENQUEUE
IF node.right ≠ NULL
queue ga node.right ENQUEUE
RETURN result
BFS davomida navbat qanday o'zgarishini kuzatish:
queue: [1] result: []
DEQUEUE 1 → result: [1]
ENQUEUE 2, 3
queue: [2, 3]
DEQUEUE 2 → result: [1, 2]
ENQUEUE 4, 5
queue: [3, 4, 5]
DEQUEUE 3 → result: [1, 2, 3]
ENQUEUE 6
queue: [4, 5, 6]
DEQUEUE 4 → result: [1, 2, 3, 4] bolasi yo'q
queue: [5, 6]
DEQUEUE 5 → result: [1, 2, 3, 4, 5] bolasi yo'q
queue: [6]
DEQUEUE 6 → result: [1, 2, 3, 4, 5, 6] bolasi yo'q
queue: []
Natija: [1, 2, 3, 4, 5, 6]
Qavat bo'yicha BFS
Ko'pincha har qavatni alohida ro'yxat sifatida olish kerak bo'ladi. Bu uchun har iteratsiyada navbatdagi barcha elementlar — joriy qavat — birgalikda qayta ishlanadi.
FUNCTION BFS_BY_LEVEL(root)
IF root = NULL
RETURN []
queue = [root]
result = []
WHILE queue bo'sh emas
level_size = navbat hajmi // joriy qavatdagi elementlar soni
current_level = []
FOR i = 0 DAN level_size-1 GACHA
node = queue dan DEQUEUE
current_level ga node.value qo'sh
IF node.left ≠ NULL
queue ga node.left ENQUEUE
IF node.right ≠ NULL
queue ga node.right ENQUEUE
result ga current_level qo'sh
RETURN result
Bu "Level Order Traversal" — keyingi masalada batafsil ko'riladi.
Vaqt va xotira murakkabligi
| Algoritm | Vaqt | Xotira | Xotira nima uchun? |
|---|---|---|---|
| DFS (rekursiv) | O(n) |
O(h) |
Call stack chuqurligi |
| DFS (iterativ) | O(n) |
O(h) |
Stek hajmi |
| BFS | O(n) |
O(w) |
Navbat hajmi |
n — tugunlar soni, h — balandlik, w — eng keng qavat.
DFS xotirasi: rekursion call stack daraxt balandligiga teng — O(h). Muvozanatlangan daraxtda O(log n), zanjir ko'rinishida O(n).
BFS xotirasi: navbatda eng ko'p tugun eng keng qavatda bo'ladi. Mukammal binar daraxtda oxirgi qavat n/2 tugunni saqlaydi — O(n/2) = O(n). Chuqur va tor daraxtda BFS xotirasi DFS'dan kamroq; keng va yassi daraxtda ko'proq.
Qaysi traversal qachon?
| Vazifa | Eng mos traversal | Sabab |
|---|---|---|
| BST'ni tartibli o'qish | Inorder | BST inorder → o'sish tartibi |
| Daraxt nusxasini olish | Preorder | Ota avval, bolalar keyin |
| Daraxtni o'chirish | Postorder | Bolalar avval, ota keyin |
| Qavat bo'yicha o'qish | BFS | Qavatma-qavat tabiiy |
| Eng qisqa yo'l | BFS | BFS birinchi topilgan yo'l qisqa |
| Barcha yo'llar | DFS | Rekursiv shox bo'yicha yurish |
| Daraxt mavjudmi (chuqur)? | DFS | Katta w da BFS xotira ko'p |
Rekursiya va DFS aloqasi
DFS rekursion yozilishi eng tabiiy. Rekursion funksiya har safar:
- Base case tekshiradi (NULL → qaytadi).
- Joriy tugunga ish qiladi (VISIT).
- Chap kenja daraxtga rekursiv chaqiradi.
- O'ng kenja daraxtga rekursiv chaqiradi.
Bu "bo'l va zabt et" tamoyili: har tugun o'z kenja daraxtini mustaqil qayta ishlaydi. Natija quyi tugunlar natijalarini birlashtiradi.
FUNCTION TREE_HEIGHT(node)
IF node = NULL
RETURN -1
left_h = TREE_HEIGHT(node.left)
right_h = TREE_HEIGHT(node.right)
RETURN 1 + MAX(left_h, right_h)
Bu postorder mantiqida: avval chap va o'ng balandligi hisoblanadi, so'ng birlashtirilib ildizga qaytariladi.
Real qo'llanishlar
Fayl tizimini ko'rsatish
ls -R yoki find buyrug'i DFS preorder ishlatadi: papka nomi avval, keyin uning bolalari.
Brauzer DOM traversali
JavaScript document.querySelectorAll DOM daraxtini DFS bilan aylanib, mos elementlarni to'playdi. CSS animatsiyasi daraxtda pastdan yuqoriga (postorder) qo'llanishi mumkin.
Grafik render tartibi
Scene graph'da (3D sahnada) ota-transformatsiya bolaga tatbiq qilish uchun preorder ishlatiladi.
Sosial tarmoq eng qisqa yo'l
"Ikki foydalanuvchi orasidagi eng qisqa bog'lanish zanjiri" — BFS. BFS daraxt yoki grafda manba dan boshlanib, eng yaqin tugunlarni avval ko'rib chiqadi.
Keng tarqalgan xatolar
NULL holat tekshirilmasa. Har rekursiv funksiya birinchi qadamida node = NULL holatini tekshirishi kerak. Tushirib qoldirilsa, null-dereference xatosi.
BFS uchun navbat o'rniga stek ishlatish. Navbat (FIFO) BFS uchun; stek (LIFO) DFS uchun. Ularni aralashtirish noto'g'ri traversal tartibini beradi.
Inorder va preorderni aralashtirish. VISIT ning joyi muhim: preorderdda rekursiv chaqiruvlardan oldin, inorderdda chap rekursiyadan keyin o'ng rekursiyadan oldin, postorderdda ikkalasidan keyin.
Qavat hajmini navbatdan dinamik o'lchash. level_size = navbat hajmi navbat to'ldirilishidan oldin olinishi kerak. Aks holda joriy qavat tugunlari qo'shilgan bolalar ham hisob-ga kirib ketishi mumkin.
Xulosa
Daraxt traversali uchun ikkita asosiy strategiya mavjud.
DFS rekursiya yordamida chuqurlikka kiradi. Uchta tartibi bor: preorder (ildiz avval), inorder (ildiz o'rtada — BST uchun muhim), postorder (ildiz oxirida — o'chirish va hisoblash uchun). Xotira O(h).
BFS navbat yordamida qavatma-qavat ishlaydi. Eng qisqa yo'l va qavat bo'yicha ishlov uchun mos. Xotira O(w) — eng keng qavat.
Ko'plab daraxt masalalari — chuqurlik, simmetriya, umumiy qiymat, yo'l — DFS bilan rekursiv yechiladi: har tugun o'z kenja daraxtlaridan natija olib, birlashtiradi. Qavat bo'yicha masalalar BFS bilan aniqroq yechiladi.