11-bo‘lim
Graflar
Graf atamalari, yo'naltirilgan va vaznli graflar, qo'shnilik matritsasi va ro'yxati, ularni tanlash mezonlari.
Ushbu bo‘lim mundarijasi
Graf - eng universal ma'lumotlar tuzilmasi. Massiv, ro'yxat va daraxtlarning barchasi - grafning maxsus holatlari.
Graf nima? #
Graf - tugunlar (vershina) va ularni bog'lovchi qirralardan iborat tuzilma.
Atrofimizdagi deyarli hamma narsa graf:
| Soha | Tugun | Qirra |
|---|---|---|
| Ijtimoiy tarmoq | Odam | Do'stlik |
| Xarita | Shahar | Yo'l |
| Internet | Sahifa | Havola |
| Metro | Bekat | Yo'nalish |
| Kompilyator | Modul | Bog'liqlik |
| Aviatsiya | Aeroport | Reys |
Asosiy atamalar #
| Atama | Ma'nosi |
|---|---|
| Vershina (tugun) | Grafning nuqtasi |
| Qirra | Ikki vershinani bog'lovchi chiziq |
| Qo'shni | Bevosita qirra bilan bog'langan vershina |
| Daraja (degree) | Vershinaga tegishli qirralar soni |
| Yo'l (path) | Vershinalar ketma-ketligi |
| Sikl | Boshlangan joyga qaytadigan yo'l |
| Bog'langan graf | Har bir vershinadan har biriga yo'l bor |
| Vazn | Qirraga biriktirilgan son (masofa, narx) |
- Daraxt = sikli yo'q bog'langan graf
- Bog'langan ro'yxat = har bir vershinada ko'pi bilan bitta chiquvchi qirra bo'lgan graf
- DAG (yo'naltirilgan asiklik graf) = sikli yo'q yo'naltirilgan graf; vazifalar bog'liqligi uchun ishlatiladi
Grafni saqlashning ikki usuli #
1. Qo'shnilik matritsasi #
n × n o'lchamli jadval. matritsa[i][j] = 1 bo'lsa, i va j bog'langan.
class MatritsaGraf:
"""Qo'shnilik matritsasi asosidagi graf."""
def __init__(self, vershinalar):
self.vershinalar = list(vershinalar)
self._indeks = {v: i for i, v in enumerate(self.vershinalar)}
n = len(self.vershinalar)
self.matritsa = [[0] * n for _ in range(n)]
def qirra_qosh(self, birinchi, ikkinchi, vazn=1):
i, j = self._indeks[birinchi], self._indeks[ikkinchi]
self.matritsa[i][j] = vazn
self.matritsa[j][i] = vazn # yo'naltirilmagan
def bogliqmi(self, birinchi, ikkinchi):
"""O(1) - matritsaning kuchli tomoni."""
return self.matritsa[self._indeks[birinchi]][self._indeks[ikkinchi]] != 0
def qoshnilar(self, vershina):
"""O(n) - butun qatorni ko'rish kerak."""
i = self._indeks[vershina]
return [self.vershinalar[j]
for j, qiymat in enumerate(self.matritsa[i]) if qiymat != 0]
def chiz(self):
print(" " + " ".join(f"{v:>2}" for v in self.vershinalar))
for i, qator in enumerate(self.matritsa):
print(f" {self.vershinalar[i]:>2} " + " ".join(f"{q:>2}" for q in qator))
graf = MatritsaGraf(["A", "B", "C", "D"])
graf.qirra_qosh("A", "B")
graf.qirra_qosh("A", "C")
graf.qirra_qosh("B", "C")
graf.qirra_qosh("C", "D")
graf.chiz()
print("\nA va B bog'liqmi:", graf.bogliqmi("A", "B"))
print("C ning qo'shnilari:", graf.qoshnilar("C"))
A B C D
A 0 1 1 0
B 1 0 1 0
C 1 1 0 1
D 0 0 1 0
A va B bog'liqmi: True
C ning qo'shnilari: ['A', 'B', 'D']
2. Qo'shnilik ro'yxati #
Har bir vershina uchun uning qo'shnilari ro'yxati saqlanadi. Amalda eng ko'p ishlatiladigan usul.
from collections import defaultdict
class Graf:
"""Qo'shnilik ro'yxati asosidagi graf."""
def __init__(self, yonaltirilgan=False):
self._qoshnilar = defaultdict(list)
self.yonaltirilgan = yonaltirilgan
def vershina_qosh(self, vershina):
_ = self._qoshnilar[vershina] # bo'sh ro'yxat yaratadi
def qirra_qosh(self, birinchi, ikkinchi, vazn=1):
"""O(1)."""
self._qoshnilar[birinchi].append((ikkinchi, vazn))
if not self.yonaltirilgan:
self._qoshnilar[ikkinchi].append((birinchi, vazn))
def qoshnilar(self, vershina):
"""O(1) - ro'yxatning kuchli tomoni."""
return self._qoshnilar[vershina]
def bogliqmi(self, birinchi, ikkinchi):
"""O(daraja) - qo'shnilarni ko'rib chiqish kerak."""
return any(q == ikkinchi for q, _ in self._qoshnilar[birinchi])
def vershinalar(self):
return list(self._qoshnilar.keys())
def qirralar_soni(self):
jami = sum(len(q) for q in self._qoshnilar.values())
return jami if self.yonaltirilgan else jami // 2
def chiz(self):
for vershina in sorted(self._qoshnilar):
bogliqlar = ", ".join(
f"{q}({v})" if v != 1 else str(q)
for q, v in self._qoshnilar[vershina]
)
print(f" {vershina} -> {bogliqlar}")
graf = Graf()
for a, b in [("A", "B"), ("A", "C"), ("B", "C"), ("C", "D")]:
graf.qirra_qosh(a, b)
graf.chiz()
print(f"\nVershinalar: {len(graf.vershinalar())}, qirralar: {graf.qirralar_soni()}")
A -> B, C
B -> A, C
C -> A, B, D
D -> C
Vershinalar: 4, qirralar: 4
Qaysi usulni tanlash kerak? #
| Mezon | Matritsa | Ro'yxat |
|---|---|---|
| Xotira | O(V²) | O(V + E) |
| Qirra bormi? | O(1) | O(daraja) |
| Qo'shnilarni olish | O(V) | O(1) |
| Qirra qo'shish | O(1) | O(1) |
| Vershina qo'shish | O(V²) | O(1) |
V - vershinalar soni, E - qirralar soni.
Real graflar siyrak bo'ladi. Facebook'da 3 milliard foydalanuvchi bor, lekin o'rtacha odamning 300 ta do'sti - 3 milliard emas. Matritsa uchun 9×10¹⁸ katak kerak bo'lardi. Shu sababli ijtimoiy tarmoqlar, xaritalar va tarmoqlar - hammasi qo'shnilik ro'yxati ishlatadi.
Lug'at bilan sodda graf #
Ko'p hollarda alohida klass ham kerak emas:
# Toshkent metrosining soddalashtirilgan sxemasi
metro = {
"Chilonzor": ["Mirzo Ulug'bek", "Novza"],
"Novza": ["Chilonzor", "Milliy bog'"],
"Milliy bog'": ["Novza", "Xalqlar do'stligi"],
"Xalqlar do'stligi": ["Milliy bog'", "Paxtakor"],
"Paxtakor": ["Xalqlar do'stligi", "Mustaqillik maydoni", "Alisher Navoiy"],
"Mustaqillik maydoni": ["Paxtakor", "Amir Temur xiyoboni"],
"Alisher Navoiy": ["Paxtakor", "G'afur G'ulom"],
"G'afur G'ulom": ["Alisher Navoiy"],
"Amir Temur xiyoboni": ["Mustaqillik maydoni"],
"Mirzo Ulug'bek": ["Chilonzor"],
}
print(f"Bekatlar soni: {len(metro)}")
print(f"Paxtakordan yo'nalishlar: {metro['Paxtakor']}")
# Eng ko'p bog'lanishga ega bekat
eng_gavjum = max(metro, key=lambda b: len(metro[b]))
print(f"Eng gavjum tugun: {eng_gavjum} ({len(metro[eng_gavjum])} ta yo'nalish)")
Bekatlar soni: 10
Paxtakordan yo'nalishlar: ['Xalqlar do'stligi', 'Mustaqillik maydoni', 'Alisher Navoiy']
Eng gavjum tugun: Paxtakor (3 ta yo'nalish)
Bu grafni 20-bo'limdagi yakuniy loyihada ishlatamiz.
Vershina darajasi #
def darajalar(graf):
"""Har bir vershinaning darajasini hisoblaydi."""
return {v: len(qoshnilar) for v, qoshnilar in graf.items()}
for bekat, daraja in sorted(darajalar(metro).items(), key=lambda j: -j[1])[:4]:
ustun = "#" * daraja
print(f"{bekat:<22}{daraja} {ustun}")
Paxtakor 3 ###
Chilonzor 2 ##
Novza 2 ##
Milliy bog' 2 ##
Yo'naltirilmagan grafda barcha darajalar yig'indisi qirralar sonining ikki barobariga teng - chunki har bir qirra ikkita vershinaning darajasiga hissa qo'shadi. Bundan qiziq xulosa kelib chiqadi: har qanday grafda toq darajali vershinalar soni juft bo'ladi.
Grafning maxsus turlari #
| Turi | Xossasi | Misol |
|---|---|---|
| To'liq graf | Har bir juftlik bog'langan | Kichik jamoa aloqalari |
| Ikki bo'lakli (bipartite) | Vershinalar ikki guruhga bo'linadi | Talaba-fan, xodim-loyiha |
| DAG | Yo'naltirilgan, sikli yo'q | Vazifalar bog'liqligi, git tarixi |
| Daraxt | Bog'langan, sikli yo'q | Fayl tizimi |
| O'rmon | Bir nechta alohida daraxt | Bog'lanmagan komponentlar |
def toliq_grafmi(graf):
"""Har bir vershina qolgan hammasi bilan bog'langanmi?"""
n = len(graf)
return all(len(qoshnilar) == n - 1 for qoshnilar in graf.values())
kichik_jamoa = {
"Husanboy": ["Sardor", "Malika"],
"Sardor": ["Husanboy", "Malika"],
"Malika": ["Husanboy", "Sardor"],
}
print("To'liq grafmi:", toliq_grafmi(kichik_jamoa))
print("Metro to'liq grafmi:", toliq_grafmi(metro))
To'liq grafmi: True
Metro to'liq grafmi: False
Grafklassigaqirra_ochir(a, b)vavershina_ochir(v)metodlarini qo'shing.- Grafni matritsa ko'rinishidan ro'yxat ko'rinishiga o'giradigan funksiya yozing.
- Berilgan graf ikki bo'lakli ekanini tekshiring (maslahat: vershinalarni ikki rangga bo'yang).
- Ijtimoiy tarmoq grafida "umumiy do'stlar" ni topadigan funksiya yozing.
Xulosa #
- Graf - vershinalar va qirralardan iborat eng universal tuzilma.
- U yo'naltirilgan/yo'naltirilmagan va vaznli/vaznsiz bo'lishi mumkin.
- Qo'shnilik matritsasi: O(V²) xotira, qirrani tekshirish O(1) - zich graflar uchun.
- Qo'shnilik ro'yxati: O(V + E) xotira, qo'shnilarni olish O(1) - amalda deyarli har doim shu.
- Python'da oddiy
dictko'p hollarda yetarli graf tuzilmasini beradi.
Keyingi bo'limda graf bo'ylab yurishning ikki asosiy usuli - BFS va DFS bilan tanishamiz.
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.