9-bo‘lim
Ikkilik qidiruv daraxti
BST qoidasi, qo'shish, qidirish va o'chirish amallari, muvozanatsizlik muammosi va o'z-o'zini muvozanatlovchi daraxtlar.
Ushbu bo‘lim mundarijasi
Oddiy daraxtda qidiruv O(n). Lekin tugunlarni tartib bilan joylashtirsak, qidiruvni O(log n) ga tushirish mumkin.
BST qoidasi #
Ikkilik qidiruv daraxti (Binary Search Tree) bitta qoidaga bo'ysunadi:
Har bir tugun uchun: chap qism daraxtdagi barcha qiymatlar undan kichik, o'ng qism daraxtdagi barcha qiymatlar undan katta.
Bu qoidadan juda muhim xossa kelib chiqadi: inorder aylanish har doim tartiblangan ketma-ketlik beradi.
BST ni amalga oshirish #
class BSTTugun:
def __init__(self, qiymat):
self.qiymat = qiymat
self.chap = None
self.ong = None
class IkkilikQidiruvDaraxti:
"""Ikkilik qidiruv daraxti."""
def __init__(self):
self.ildiz = None
self._soni = 0
# ---------- qo'shish ----------
def qosh(self, qiymat):
"""Yangi qiymat qo'shadi. O'rtacha O(log n)."""
self.ildiz = self._qosh(self.ildiz, qiymat)
def _qosh(self, tugun, qiymat):
if tugun is None:
self._soni += 1
return BSTTugun(qiymat)
if qiymat < tugun.qiymat:
tugun.chap = self._qosh(tugun.chap, qiymat)
elif qiymat > tugun.qiymat:
tugun.ong = self._qosh(tugun.ong, qiymat)
# teng bo'lsa - hech narsa qilmaymiz (takror qabul qilinmaydi)
return tugun
# ---------- qidirish ----------
def bormi(self, qiymat):
"""Qiymat mavjudligini tekshiradi. O'rtacha O(log n)."""
joriy = self.ildiz
while joriy is not None:
if qiymat == joriy.qiymat:
return True
joriy = joriy.chap if qiymat < joriy.qiymat else joriy.ong
return False
# ---------- eng kichik / eng katta ----------
def eng_kichik(self):
"""Eng chapdagi tugun. O(log n)."""
if self.ildiz is None:
return None
joriy = self.ildiz
while joriy.chap is not None:
joriy = joriy.chap
return joriy.qiymat
def eng_katta(self):
"""Eng o'ngdagi tugun. O(log n)."""
if self.ildiz is None:
return None
joriy = self.ildiz
while joriy.ong is not None:
joriy = joriy.ong
return joriy.qiymat
# ---------- o'chirish ----------
def ochir(self, qiymat):
"""Qiymatni o'chiradi. O'rtacha O(log n)."""
self.ildiz, ochirildi = self._ochir(self.ildiz, qiymat)
if ochirildi:
self._soni -= 1
return ochirildi
def _ochir(self, tugun, qiymat):
if tugun is None:
return None, False
if qiymat < tugun.qiymat:
tugun.chap, ochirildi = self._ochir(tugun.chap, qiymat)
return tugun, ochirildi
if qiymat > tugun.qiymat:
tugun.ong, ochirildi = self._ochir(tugun.ong, qiymat)
return tugun, ochirildi
# topildi - uchta holat
if tugun.chap is None: # 1) bolasi yo'q yoki faqat o'ng
return tugun.ong, True
if tugun.ong is None: # 2) faqat chap bola
return tugun.chap, True
# 3) ikkala bolasi bor: o'ng qism daraxtdagi eng kichigini olamiz
vorislovchi = tugun.ong
while vorislovchi.chap is not None:
vorislovchi = vorislovchi.chap
tugun.qiymat = vorislovchi.qiymat
tugun.ong, _ = self._ochir(tugun.ong, vorislovchi.qiymat)
return tugun, True
# ---------- yordamchi ----------
def tartiblangan(self):
"""Inorder aylanish - tartiblangan ro'yxat."""
natija = []
def yur(tugun):
if tugun is None:
return
yur(tugun.chap)
natija.append(tugun.qiymat)
yur(tugun.ong)
yur(self.ildiz)
return natija
def balandlik(self):
def olcha(tugun):
if tugun is None:
return -1
return 1 + max(olcha(tugun.chap), olcha(tugun.ong))
return olcha(self.ildiz)
def __len__(self):
return self._soni
def __contains__(self, qiymat):
return self.bormi(qiymat)
Sinab ko'ramiz:
daraxt = IkkilikQidiruvDaraxti()
for son in [50, 30, 70, 20, 40, 80, 60]:
daraxt.qosh(son)
print("Tartiblangan:", daraxt.tartiblangan())
print("Balandlik: ", daraxt.balandlik())
print("Eng kichik: ", daraxt.eng_kichik())
print("Eng katta: ", daraxt.eng_katta())
print("40 bormi: ", 40 in daraxt)
print("45 bormi: ", 45 in daraxt)
daraxt.ochir(30)
print("30 o'chirildi:", daraxt.tartiblangan())
Tartiblangan: [20, 30, 40, 50, 60, 70, 80]
Balandlik: 2
Eng kichik: 20
Eng katta: 80
40 bormi: True
45 bormi: False
30 o'chirildi: [20, 40, 50, 60, 70, 80]
Qidiruv qanday ishlaydi? #
Har bir solishtirishda daraxtning yarmi tashlab yuboriladi:
O'chirishning uchta holati #
O'chirish - BST dagi eng murakkab amal. Uchta vaziyat bor:
| Holat | Nima qilinadi |
|---|---|
| Barg (bolasi yo'q) | Shunchaki o'chiriladi |
| Bitta bolasi bor | Bola otasining o'rniga ko'tariladi |
| Ikkita bolasi bor | O'ng qism daraxtdagi eng kichik qiymat o'rniga qo'yiladi |
U inorder vorisi deb ataladi - tartiblangan ketma-ketlikda o'chirilayotgan qiymatdan keyingi qiymat. Uni o'rniga qo'yganda BST qoidasi buzilmaydi: u chapdagi hamma narsadan katta va o'ngdagi qolgan hamma narsadan kichik. Chap qism daraxtdagi eng kattasi ham xuddi shunday ishlaydi.
Muvozanatsizlik muammosi #
BST ning eng katta zaifligi shu. Tartiblangan ma'lumot qo'shsangiz, daraxt bog'langan ro'yxatga aylanadi:
yaxshi = IkkilikQidiruvDaraxti()
for son in [50, 30, 70, 20, 40, 60, 80]:
yaxshi.qosh(son)
yomon = IkkilikQidiruvDaraxti()
for son in [20, 30, 40, 50, 60, 70, 80]: # tartiblangan!
yomon.qosh(son)
print(f"Aralash tartibda: balandlik = {yaxshi.balandlik()}")
print(f"Tartiblangan: balandlik = {yomon.balandlik()}")
Aralash tartibda: balandlik = 2
Tartiblangan: balandlik = 6
Ma'lumotlar bazasidan ORDER BY id bilan olingan yozuvlarni BST ga ketma-ket qo'shsangiz,
daraxtingiz kafolatli buzilgan bo'ladi. Bu real loyihalarda tez-tez uchraydigan xato.
Yechim: o'z-o'zini muvozanatlovchi daraxtlar #
| Daraxt turi | G'oyasi | Qayerda ishlatiladi |
|---|---|---|
| AVL | Balandlik farqi ≤ 1, qat'iy muvozanat | Ko'p o'qiladigan ma'lumot |
| Qizil-qora (Red-Black) | Yumshoqroq muvozanat, tez qo'shish | Java TreeMap, C++ std::map, Linux yadrosi |
| B-daraxt | Bir tugunda ko'p kalit | Ma'lumotlar bazasi indekslari |
| Treap | Tasodifiy prioritetlar | Sodda amalga oshirish |
Ular qo'shish yoki o'chirishdan keyin aylantirish (rotation) amali bilan muvozanatni tiklaydi:
Murakkablik jadvali #
| Amal | O'rtacha | Eng yomon (muvozanatsiz) | Muvozanatlangan |
|---|---|---|---|
| Qidiruv | O(log n) | O(n) | O(log n) |
| Qo'shish | O(log n) | O(n) | O(log n) |
| O'chirish | O(log n) | O(n) | O(log n) |
| Eng kichik/katta | O(log n) | O(n) | O(log n) |
| Tartiblangan chiqarish | O(n) | O(n) | O(n) |
BST va xesh-jadval: qaysi biri? #
| Xususiyat | Xesh-jadval | BST |
|---|---|---|
| Qidiruv | O(1) | O(log n) |
| Tartib saqlanadimi | Yo'q | Ha |
| Eng kichik/katta | O(n) | O(log n) |
| Oraliq so'rov (10-50) | O(n) | O(log n + k) |
| Eng yomon holat | O(n) | O(log n)* |
| Xotira | Ko'proq | Kamroq |
* muvozanatlangan daraxtda
- Faqat "bormi / yo'qmi" kerakmi → xesh-jadval (
dict,set). - Tartib, oraliq so'rov yoki "eng yaqin qiymat" kerakmi → daraxt.
Aynan shuning uchun ma'lumotlar bazalari indeks uchun xesh emas, B-daraxt ishlatadi:
WHERE narx BETWEEN 10000 AND 50000 kabi so'rovlar xeshda ishlamaydi.
Amaliy misol: avtomatik to'ldirish #
BST oraliq so'rovlarda kuchli. Prefiks bo'yicha so'zlarni topamiz:
def prefiks_boyicha(daraxt, prefiks):
"""Berilgan prefiks bilan boshlanadigan barcha so'zlarni topadi."""
natija = []
def yur(tugun):
if tugun is None:
return
# Chapga borish mantiqiymi?
if tugun.qiymat >= prefiks:
yur(tugun.chap)
if tugun.qiymat.startswith(prefiks):
natija.append(tugun.qiymat)
# O'ngga borish mantiqiymi?
if tugun.qiymat <= prefiks + "":
yur(tugun.ong)
yur(daraxt.ildiz)
return natija
lugat = IkkilikQidiruvDaraxti()
for soz in ["dastur", "dasturchi", "dasturlash", "daftar", "daraxt", "kitob", "kalit"]:
lugat.qosh(soz)
print("'dastur' bilan boshlanuvchilar:")
for soz in prefiks_boyicha(lugat, "dastur"):
print(" -", soz)
'dastur' bilan boshlanuvchilar:
- dastur
- dasturchi
- dasturlash
bst_mi(tugun)funksiyasini yozing - berilgan daraxt BST qoidasiga bo'ysunishini tekshiring. (Diqqat: faqat bevosita bolalarni tekshirish yetarli emas.)k_chi_eng_kichik(daraxt, k)- daraxtdagi k-chi eng kichik qiymatni toping.- Tartiblangan massivdan muvozanatli BST quring (maslahat: o'rtadagi elementni ildiz qiling).
- Ikkita tugunning eng yaqin umumiy ajdodini (LCA) toping.
Xulosa #
- BST qoidasi: chapda kichiklar, o'ngda kattalar.
- Inorder aylanish har doim tartiblangan ketma-ketlik beradi.
- Qidiruv, qo'shish va o'chirish o'rtacha O(log n) - har qadamda yarmi tashlanadi.
- O'chirishda ikkita bolasi bor tugun inorder vorisi bilan almashtiriladi.
- Tartiblangan ma'lumot BST ni buzadi - u bog'langan ro'yxatga aylanadi.
- Amalda AVL, qizil-qora yoki B-daraxt kabi o'z-o'zini muvozanatlovchi turlar ishlatiladi.
Keyingi bo'limda maxsus daraxt - uyum va uning ustiga qurilgan prioritetli navbatni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.