Visaptverošs ceļvedis koku šķērsošanas algoritmiem: Dziļuma meklēšana (DFS) un Platuma meklēšana (BFS). Apgūstiet to principus, ieviešanu, pielietojumu un veiktspējas raksturlielumus.
Koku šķērsošanas algoritmi: Dziļuma meklēšana (DFS) pret Platuma meklēšanu (BFS)
Datorzinātnē koku šķērsošana (zināms arī kā koku meklēšana vai koku apstaigāšana) ir process, kurā tiek apmeklēts (pārbaudīts un/vai atjaunināts) katrs mezgls koka datu struktūrā, tieši vienu reizi. Koki ir fundamentālas datu struktūras, ko plaši izmanto dažādos lietojumos, sākot no hierarhisku datu attēlošanas (piemēram, failu sistēmas vai organizatoriskās struktūras) līdz efektīvas meklēšanas un šķirošanas algoritmu atvieglošanai. Izpratne par to, kā šķērsot koku, ir būtiska, lai efektīvi strādātu ar tiem.
Divas galvenās pieejas koku šķērsošanai ir Dziļuma meklēšana (DFS) un Platuma meklēšana (BFS). Katrs algoritms piedāvā atšķirīgas priekšrocības un ir piemērots dažāda veida problēmām. Šis visaptverošais ceļvedis detalizēti izpētīs gan DFS, gan BFS, aptverot to principus, ieviešanu, pielietojumu un veiktspējas raksturlielumus.
Izpratne par koku datu struktūrām
Pirms iedziļināties šķērsošanas algoritmos, īsi pārskatīsim koku datu struktūru pamatus.
Kas ir koks?
Koks ir hierarhiska datu struktūra, kas sastāv no mezgliem, kurus savieno malas. Tam ir saknes mezgls (augšējais mezgls), un katram mezglam var būt nulle vai vairāki bērnu mezgli. Mezglus bez bērniem sauc par lapu mezgliem. Galvenās koka īpašības ietver:
- Sakne: Augšējais mezgls kokā.
- Mezgls: Elements kokā, kas satur datus un, iespējams, atsauces uz bērnu mezgliem.
- Mala: Savienojums starp diviem mezgliem.
- Vecāks: Mezgls, kuram ir viens vai vairāki bērnu mezgli.
- Bērns: Mezgls, kas ir tieši savienots ar citu mezglu (tā vecāku) kokā.
- Lapa: Mezgls bez bērniem.
- Apakškoks: Koks, ko veido mezgls un visi tā pēcnācēji.
- Mezgla dziļums: Malu skaits no saknes līdz mezglam.
- Koka augstums: Jebkura mezgla maksimālais dziļums kokā.
Koku veidi
Pastāv vairāki koku varianti, katram no tiem ir specifiskas īpašības un pielietojumi. Daži izplatītākie veidi ietver:
- Binārais koks: Koks, kurā katram mezglam ir ne vairāk kā divi bērni, ko parasti sauc par kreiso bērnu un labo bērnu.
- Binārais meklēšanas koks (BST): Binārais koks, kurā katra mezgla vērtība ir lielāka vai vienāda ar visu mezglu vērtību tā kreisajā apakškokā un mazāka vai vienāda ar visu mezglu vērtību tā labajā apakškokā. Šī īpašība nodrošina efektīvu meklēšanu.
- AVL koks: Pašbalansējošs binārais meklēšanas koks, kas uztur līdzsvarotu struktūru, lai nodrošinātu logaritmisku laika sarežģītību meklēšanas, ievietošanas un dzēšanas operācijām.
- Sarkanmelns koks: Vēl viens pašbalansējošs binārais meklēšanas koks, kas izmanto krāsu īpašības, lai uzturētu līdzsvaru.
- N-ārais koks (vai K-ārais koks): Koks, kurā katram mezglam var būt ne vairāk kā N bērnu.
Dziļuma meklēšana (DFS)
Dziļuma meklēšana (DFS) ir koku šķērsošanas algoritms, kas izpēta pēc iespējas tālāk katru zaru, pirms atgriežas atpakaļ. Tas prioritizē iešanu dziļi kokā pirms brāļu un māsu izpētes. DFS var ieviest rekursīvi vai iteratīvi, izmantojot steku.DFS algoritmi
Pastāv trīs izplatīti DFS šķērsošanas veidi:
- Inorder šķērsošana (Kreisais-Sakne-Labais): Apmeklē kreiso apakškoku, pēc tam saknes mezglu un visbeidzot labo apakškoku. To parasti izmanto binārajiem meklēšanas kokiem, jo tas apmeklē mezglus sakārtotā secībā.
- Preorder šķērsošana (Sakne-Kreisais-Labais): Apmeklē saknes mezglu, pēc tam kreiso apakškoku un visbeidzot labo apakškoku. To bieži izmanto, lai izveidotu koka kopiju.
- Postorder šķērsošana (Kreisais-Labais-Sakne): Apmeklē kreiso apakškoku, pēc tam labo apakškoku un visbeidzot saknes mezglu. To parasti izmanto, lai izdzēstu koku.
Ieviešanas piemēri (Python)
Šeit ir Python piemēri, kas demonstrē katru DFS šķērsošanas veidu:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
# Inorder Traversal (Left-Root-Right)
def inorder_traversal(root):
if root:
inorder_traversal(root.left)
print(root.data, end=" ")
inorder_traversal(root.right)
# Preorder Traversal (Root-Left-Right)
def preorder_traversal(root):
if root:
print(root.data, end=" ")
preorder_traversal(root.left)
preorder_traversal(root.right)
# Postorder Traversal (Left-Right-Root)
def postorder_traversal(root):
if root:
postorder_traversal(root.left)
postorder_traversal(root.right)
print(root.data, end=" ")
# Example Usage
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
print("Inorder traversal:")
inorder_traversal(root) # Output: 4 2 5 1 3
print("\nPreorder traversal:")
preorder_traversal(root) # Output: 1 2 4 5 3
print("\nPostorder traversal:")
postorder_traversal(root) # Output: 4 5 2 3 1
Iteratīva DFS (ar steku)
DFS var ieviest arī iteratīvi, izmantojot steku. Šeit ir iteratīvas preorder šķērsošanas piemērs:
def iterative_preorder(root):
if root is None:
return
stack = [root]
while stack:
node = stack.pop()
print(node.data, end=" ")
# Push right child first so left child is processed first
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
#Example Usage (same tree as before)
print("\nIterative Preorder traversal:")
iterative_preorder(root)
DFS pielietojumi
- Ceļa atrašana starp diviem mezgliem: DFS var efektīvi atrast ceļu grafā vai kokā. Apsveriet datu pakešu maršrutēšanu tīklā (attēlotu kā grafu). DFS var atrast maršrutu starp diviem serveriem, pat ja pastāv vairāki maršruti.
- Topoloģiskā šķirošana: DFS izmanto topoloģiskai orientētu aciklisku grafu (DAG) šķirošanai. Iedomājieties uzdevumu plānošanu, kur daži uzdevumi ir atkarīgi no citiem. Topoloģiskā šķirošana sakārto uzdevumus tādā secībā, kas ievēro šīs atkarības.
- Ciklu noteikšana grafā: DFS var noteikt ciklus grafā. Ciklu noteikšana ir svarīga resursu sadalē. Ja process A gaida procesu B un process B gaida procesu A, tas var izraisīt strupceļu.
- Labirintu risināšana: DFS var izmantot, lai atrastu ceļu cauri labirintam.
- Izteiksmju parsēšana un novērtēšana: Kompilatori izmanto uz DFS balstītas pieejas matemātisku izteiksmju parsēšanai un novērtēšanai.
DFS priekšrocības un trūkumi
Priekšrocības:
- Vienkārši ieviest: Rekursīvā ieviešana bieži ir ļoti kodolīga un viegli saprotama.
- Atmiņas ziņā efektīvs noteiktiem kokiem: DFS prasa mazāk atmiņas nekā BFS dziļi ligzdotiem kokiem, jo tam ir jāglabā tikai mezgli pašreizējā ceļā.
- Var ātri atrast risinājumus: Ja vēlamais risinājums ir dziļi kokā, DFS var to atrast ātrāk nekā BFS.
Trūkumi:
- Nav garantēts, ka tiks atrasts īsākais ceļš: DFS var atrast ceļu, bet tas var nebūt īsākais ceļš.
- Potenciāls bezgalīgiem cikliem: Ja koks nav rūpīgi strukturēts (piemēram, satur ciklus), DFS var iestrēgt bezgalīgā ciklā.
- Stack Overflow: Rekursīvā ieviešana var izraisīt stack overflow kļūdas ļoti dziļiem kokiem.
Platuma meklēšana (BFS)
Platuma meklēšana (BFS) ir koku šķērsošanas algoritms, kas izpēta visus kaimiņu mezglus pašreizējā līmenī, pirms pāriet uz mezgliem nākamajā līmenī. Tas izpēta koku līmeni pa līmenim, sākot no saknes. BFS parasti tiek ieviests iteratīvi, izmantojot rindu.
BFS algoritms
- Ievietojiet rindā saknes mezglu.
- Kamēr rinda nav tukša:
- Izņemiet mezglu no rindas.
- Apmeklējiet mezglu (piemēram, izdrukājiet tā vērtību).
- Ievietojiet rindā visus mezgla bērnus.
Ieviešanas piemērs (Python)
from collections import deque
def bfs_traversal(root):
if root is None:
return
queue = deque([root])
while queue:
node = queue.popleft()
print(node.data, end=" ")
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
#Example Usage (same tree as before)
print("BFS traversal:")
bfs_traversal(root) # Output: 1 2 3 4 5
BFS pielietojumi
- Īsākā ceļa atrašana: BFS garantē, ka atradīs īsāko ceļu starp diviem mezgliem nesvērtā grafā. Iedomājieties sociālo tīklu vietnes. BFS var atrast īsāko savienojumu starp diviem lietotājiem.
- Grafu šķērsošana: BFS var izmantot, lai šķērsotu grafu.
- Tīmekļa rāpošana: Meklētājprogrammas izmanto BFS, lai rāpotu tīmeklī un indeksētu lapas.
- Tuvāko kaimiņu atrašana: Ģeogrāfiskajā kartēšanā BFS var atrast tuvākos restorānus, degvielas uzpildes stacijas vai slimnīcas noteiktai vietai.
- Plūdu aizpildīšanas algoritms: Attēlu apstrādē BFS veido pamatu plūdu aizpildīšanas algoritmiem (piemēram, rīkam "krāsas spainis").
BFS priekšrocības un trūkumi
Priekšrocības:
- Garantēts, ka tiks atrasts īsākais ceļš: BFS vienmēr atrod īsāko ceļu nesvērtā grafā.
- Piemērots tuvāko mezglu atrašanai: BFS ir efektīvs, lai atrastu mezglus, kas atrodas tuvu sākuma mezglam.
- Izvairās no bezgalīgiem cikliem: Tā kā BFS izpēta līmeni pa līmenim, tas izvairās no iestrēgšanas bezgalīgos ciklos, pat grafos ar cikliem.
Trūkumi:
- Atmiņas ietilpīgs: BFS var prasīt daudz atmiņas, īpaši platiem kokiem, jo tam ir jāglabā visi mezgli pašreizējā līmenī rindā.
- Var būt lēnāks par DFS: Ja vēlamais risinājums ir dziļi kokā, BFS var būt lēnāks par DFS, jo tas izpēta visus mezglus katrā līmenī pirms iešanas dziļāk.
DFS un BFS salīdzināšana
Šeit ir tabula, kurā apkopotas galvenās atšķirības starp DFS un BFS:
| Funkcija | Dziļuma meklēšana (DFS) | Platuma meklēšana (BFS) |
|---|---|---|
| Šķērsošanas secība | Izpēta pēc iespējas tālāk katru zaru pirms atgriešanās atpakaļ | Izpēta visus kaimiņu mezglus pašreizējā līmenī, pirms pāriet uz nākamo līmeni |
| Ieviešana | Rekursīvs vai iteratīvs (ar steku) | Iteratīvs (ar rindu) |
| Atmiņas izmantošana | Parasti mazāk atmiņas (dziļiem kokiem) | Parasti vairāk atmiņas (platiem kokiem) |
| Īsākais ceļš | Nav garantēts, ka tiks atrasts īsākais ceļš | Garantēts, ka tiks atrasts īsākais ceļš (nesvērtos grafos) |
| Pielietojumi | Ceļu atrašana, topoloģiskā šķirošana, ciklu noteikšana, labirintu risināšana, izteiksmju parsēšana | Īsākā ceļa atrašana, grafu šķērsošana, tīmekļa rāpošana, tuvāko kaimiņu atrašana, plūdu aizpildīšana |
| Bezgalīgu ciklu risks | Lielāks risks (nepieciešama rūpīga strukturēšana) | Mazāks risks (izpēta līmeni pa līmenim) |
Izvēle starp DFS un BFS
Izvēle starp DFS un BFS ir atkarīga no konkrētās problēmas, kuru mēģināt atrisināt, un koka vai grafa īpašībām, ar kuru strādājat. Šeit ir daži norādījumi, kas palīdzēs jums izvēlēties:
- Izmantojiet DFS, kad:
- Koks ir ļoti dziļš un jūs domājat, ka risinājums ir dziļi lejā.
- Atmiņas izmantošana ir galvenā problēma, un koks nav pārāk plats.
- Jums jānosaka cikli grafā.
- Izmantojiet BFS, kad:
- Jums jāatrod īsākais ceļš nesvērtā grafā.
- Jums jāatrod tuvākie mezgli sākuma mezglam.
- Atmiņa nav galvenais ierobežojums, un koks ir plats.
Ārpus binārajiem kokiem: DFS un BFS grafos
Lai gan mēs galvenokārt esam apsprieduši DFS un BFS koku kontekstā, šie algoritmi ir vienlīdz piemērojami grafiem, kas ir vispārīgākas datu struktūras, kur mezgliem var būt patvaļīgi savienojumi. Galvenie principi paliek nemainīgi, bet grafi var ieviest ciklus, pieprasot papildu uzmanību, lai izvairītos no bezgalīgiem cikliem.
Piemērojot DFS un BFS grafiem, ir ierasts uzturēt "apmeklētu" kopu vai masīvu, lai sekotu līdzi mezgliem, kas jau ir izpētīti. Tas neļauj algoritmam atkārtoti apmeklēt mezglus un iestrēgt ciklos.