Tema
/
Algoritmai ir duomenų struktūros GO
Pamoka 11 / 15 Dvejetainiai medžiai ir BST ~50 min
Teorija

Invariantas ir keturi apėjimai

Visa struktūra remiasi vienu sakiniu.

BST invariantas

Kiekvienam mazgui: visi kairiojo pomedžio raktai yra mažesni už jo raktą, visi dešiniojo — didesni.

Ne „vaikas kairėje mažesnis", o visas pomedis. Iš to plaukia viskas kita.

Mazgas turi raktą ir dvi rodykles:

node:
    key    raktas (pavadinimas)
    item   įrašas
    left   rodyklė į mažesniuosius
    right  rodyklė į didesniuosius

Dešimtoje pamokoje mazgas turėjo vieną rodyklę (next grandinėlėje). Čia jų dvi — ir šeštame žingsnyje pamatysi, kiek tai kainuoja atmintimi.

Paieška

Kiekvienas palyginimas atmeta visą pomedį:

Get(title):
    cur ← root
    kol cur ≠ nil:
        c.Hit()
        jei title < cur.key: cur ← cur.left
        jei title > cur.key: cur ← cur.right
        kitaip:              grąžink cur.item, true
    grąžink tuščią, false

Tai šeštos pamokos dvejetainė paieška, tik kelias iš anksto įrašytas į rodykles, o ne skaičiuojamas iš indeksų. Ta pati mintis: vienas palyginimas perpus sumažina liekančią erdvę.

„Perpus" — jeigu pomedžiai vienodo dydžio. Kai jie ne, kaina kyla, ir septintas žingsnis parodo, kaip toli tai gali nueiti.

Įterpimas

Įterpimas — ta pati paieška, tik baigiama ne radus, o pasiekus tuščią vietą:

Put(item):
    ieškok item.Title lygiai kaip Get
    jei radai: pakeisk įrašą (vienodas raktas, kaip 10 pamokoje)
    jei priėjai nil: čia ir įrašyk naują mazgą

Naujas mazgas visada tampa lapu. Medis niekada nesitvarko iš naujo — todėl įterpimo tvarka nulemia formą, ir todėl septintas žingsnis apskritai įmanomas.

Apėjimas didėjimo tvarka (in-order)

Pirmojo žingsnio atsakymas, trimis eilutėmis:

walk(n):
    jei n = nil: grįžk
    walk(n.left)      // pirma visi mažesni
    išvesk n.key      // paskui aš
    walk(n.right)     // paskui visi didesni

Iš invarianto seka tiesiogiai: kairysis pomedis yra visi mažesni raktai, tad išvedus juos pirma, tada save, tada visus didesnius, gaunama didėjimo tvarka. O(n), be papildomos atminties ir be nė vieno palyginimo.

Rėžio užklausa

Tas pats apėjimas, tik su dviem sąlygomis, kurios nukerta nereikalingas šakas:

Range(lo, hi, n):
    jei n = nil: grįžk
    jei lo < n.key:            Range(lo, hi, n.left)    // kairėje gali būti
    jei lo ≤ n.key ≤ hi:       išvesk n.key
    jei n.key < hi:            Range(lo, hi, n.right)   // dešinėje gali būti

Būtent tos dvi jei sąlygos pavertė pirmo žingsnio 1 000 mazgų į 11: kai n.key jau didesnis už hi, visame dešiniajame pomedyje atsakymo būti negali, tad į jį net neužeinama.

Maišos lentelė tokio sprendimo priimti negali. Ji nežino, kas yra „didesnis".