Cvičenie 4 – Stromy

Kód z prednášky

Ciele cvičení:

  • vedieť, ako v programoch uložiť a reprezentovať hierarchické údaje a štruktúry,
  • vedieť riešiť jednoduché úlohy s hierarchickými údajovými štruktúrami,
  • zoznámiť sa s aritmetickými stromami,
  • poznať binárne vyhľadávacie stromy a základné operácie nad nimi (test na prítomnosť hodnoty, pridanie a odobranie hodnoty),
  • vedieť analyzovať časovú zložitosť jednotlivých algoritmov.

Strom potomkov

  • Do triedy Osoba z prednášky pridajte metódu pocetGeneracii, ktorá vráti počet generácií potomkov danej osoby.

1
public int pocetGeneracii();
  • Do triedy Osoba z prednášky pridajte metódu pridajDoZoznamu, ktorá uloží všetky osoby stromu potomkov danej osoby do zoznamu osôb.

1
public void pridajDoZoznamu(List<Osoba> zoznam);

Súbory

  • Naprogramujte metódu, ktorá zráta celkovú veľkosť súborov, ktoré sa nachádzajú v zadanom adresári a jeho podadresároch.

1
public long velkostSuborov(File adresar);

Binárne stromy

  • Nakreslite nejaký binárny strom s 10 uzlami s číselnými hodnotami v uzloch. Popíšte tento strom z hľadiska „stromových“ parametrov (výška, koreň, listy, vnútorné uzly, …).
  • Do triedy Uzol (z prednášky) pridajte statickú metódu, ktorá vráti referenciu na koreň stromu, ktorého opis je v zadanom reťazci.

1
public static Uzol stromZRetazca(String opisStromu)

Opis stromu je reťazec tvaru: (L) h (P), kde L je opis ľavého podstromu, P je opis pravého podstromu a h je hodnota v koreni stromu.
Reťazec ((2) 7 ((5) 6 (11))) 2 (5 ((4) 9)) reprezentuje strom:

  • Do triedy Uzol (z prednášky) pridajte metódu ktorá pre zadaný uzol vráti súčet hodnôt uložených v uzloch stromu, ktorého je zadaný uzol koreňom.
  • Do triedy Uzol (z prednášky) pridajte metódy na výpočet minimálnej uloženej hodnoty, počtu uložených hodnôt a výšku stromu.
  • Predpokladajte, že poznáte postupnosť inorder a preorder spracovania hodnôt v binárnom strome, ktorý v každom uzle uchováva inú hodnotu. Navrhnite postup, ako zo znalosti týchto 2 postupností zrekonštruovať binárny strom. pre náročnejších: Ako by ste túto úlohu riešili pri znalosti postupností postorder a preorder prechodu?
  • Aký je minimálny a maximálny počet uzlov stromu s výškou h? Svoje tvrdenie dokážte (formálny dôkaz matematickou indukciou na výšku stromu h).
    • Dôsledok: Aká je minimálna možná výška a aká je maximálna možná výška binárneho stromu s n uzlami?

Binárne vyhľadávacie stromy

  • Pre náhodné vytvorenú postupnosť 10 čísel nakreslite binárny strom uchovávajúci tieto hodnoty, ktorý je navyše aj binárnym vyhľadávacím stromom.
  • Odsimulujte algoritmy na testovanie prítomnosti hodnoty, pridávanie a odoberanie hodnoty.
  • Navrhnite (a implementujte) algoritmus na overenie, či nejaký binárny strom je binárny vyhľadávací strom.
  • Akú postupnosť hodnôt dostaneme pri inorder prechode binárneho vyhľadávacieho stromu a prečo?
  • Napíšte metódu, ktorá v binárnom vyhľadávacom strome nájde minimálnu hodnotu.
  • Vyskúšajte https://people.ksp.sk/~kuko/gnarley-trees/Intro.html
  • Je nižšie uvedená implementácia zistenia výskytu hodnoty v BVS korektná? Ak áno, vyargumentujte. Ak nie, nájdite taký BVS, na ktorom metóda zlyhá.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean contains(int hodnota) {
    Uzol aktualny = koren;
    while (aktualny != null) {
        if (aktualny.hodnota == hodnota)
            return true;

        if (hodnota < aktualny.hodnota)
            aktualny = aktualny.lavy;

                if (aktualny.hodnota < hodnota)
            aktualny = aktualny.pravy;
    }

    return false;
}

Aritmetické stromy

  • Pre zadaný aritmetický výraz nakreslite prislúchajúci strom tohto výrazu.
  • Čo je výsledkom postupnosti inorder (postorder) prechodu tohto stromu? Rozdiskutujte zápis výrazov v postfixovej notácii.
  • Do triedy Vyraz pridajte metódy na výpis výrazu v postfixovej notácii a vyhodnotenie výrazu.
  • Pre fajnšmekrov: Rozšírte triedu Vyraz o podporu premenných vo výraze.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
 * Trieda reprezentujuca aritmeticky vyraz
 */

public class Vyraz {

    /**
     * Rozhranie, ktore implementuju vsetky uzly aritmetickeho stromu
     */

    private interface Clen {
        @Override
        public String toString();
    }

    /**
     * Trieda implementujuca uzol stromu aritmetickeho stromu, ktory
     * reprezentuje binarnu operaciu
     */

    private static class BinarnaOperacia implements Clen {
        private char operator;
        private Clen lavyPodvyraz;
        private Clen pravyPodvyraz;

        public BinarnaOperacia(char operator, Clen lavy, Clen pravy) {
            this.operator = operator;
            this.lavyPodvyraz = lavy;
            this.pravyPodvyraz = pravy;
        }

        @Override
        public String toString() {
            return "(" + lavyPodvyraz.toString() + operator
                    + pravyPodvyraz.toString() + ")";
        }
    }

    /**
     * Trieda implementujuca uzol aritmetickeho stromu, ktory reprezentuje
     * konstantu
     */

    private static class Hodnota implements Clen {
        private double hodnota;

        public Hodnota(double hodnota) {
            this.hodnota = hodnota;
        }

        @Override
        public String toString() {
            return Double.toString(hodnota);
        }
    }

    /**
     * Symboly operacii od najnizsej priority po najvyssiu
     */

    private static final String SYMBOLY_OPERACII = "+-*/";

    /**
     * Prevedie zadany vyraz do aritmetickeho stromu
     *
     * @param vyraz
     *            vyraz, ktory sa ma parsovat
     * @return referencia na koren aritmetickeho stromu
     */

    private static Clen prevedNaStrom(String vyraz) {
        // Odstranime zbytocne medzery
        vyraz = vyraz.trim();

        // Najdeme operator s najnizsou prioritou, ktory nie je v zatvorkach
        int operatorIdx = Integer.MAX_VALUE;
        int operatorPoz = -1;
        int pocitadloZatvoriek = 0;

        for (int i = 0; i < vyraz.length(); i++) {
            char znak = vyraz.charAt(i);
            if (znak == '(')
                pocitadloZatvoriek++;
            if (znak == ')')
                pocitadloZatvoriek--;
            int priorita = SYMBOLY_OPERACII.indexOf(znak);
            if ((priorita != -1) && (pocitadloZatvoriek == 0) && (i > 0)) {
                if (priorita <= operatorIdx) {
                    operatorIdx = priorita;
                    operatorPoz = i;
                }
            }
        }

        // Rozdelime vyraz na podvyrazy
        if (operatorPoz != -1) {
            return new BinarnaOperacia(SYMBOLY_OPERACII.charAt(operatorIdx),
                    prevedNaStrom(vyraz.substring(0, operatorPoz)),
                    prevedNaStrom(vyraz.substring(operatorPoz + 1)));
        }

        // Poznamka: Ak sme nenasli operator, tak je to alebo konstanta, alebo
        // cely vyraz je ozatvorkovany

        // Ak je cely vyraz ozatvorkovany, tak nechame rozparsovat jeho vnutornu
        // cast
        if ((vyraz.charAt(0) == '(')
                && (vyraz.charAt(vyraz.length() - 1) == ')'))
            return prevedNaStrom(vyraz.substring(1, vyraz.length() - 1));

        // Ak sme tu, tak to musi byt cislo
        try {
            return new Hodnota(Double.parseDouble(vyraz));
        } catch (NumberFormatException e) {
            throw new RuntimeException(
                    "Zadany vyraz nie je korektny aritmeticky vyraz");
        }
    }

    /**
     * Koren aritmetickeho stromu vyrazu
     */

    private Clen koren;

    /**
     * Skontruuje novy aritmeticky vyraz
     *
     * @param vyraz
     *            retazec s aritmetickym vyrazom
     */

    public Vyraz(String vyraz) {
        koren = prevedNaStrom(vyraz);
    }

    @Override
    public String toString() {
        return koren.toString();
    }

    public static void main(String[] args) {
        Vyraz v = new Vyraz("(-3)+6/2+3*2");
        System.out.println(v);
    }
}

Pre fajnšmekrov:

  • Formálne (=matematicky, napr. priamy dôkaz) dokážte:
    • Ak limn→∞ f(n)/g(n) = c, kde c je z R, potom f(n) = O(g(n))
  • Ukážte, že predošlá implikácia nemôže byť zosilnená na ekvivalenciu tak, že skonštruujete algoritmus, ktorého časová zložitosť v najhoršom prípade na vstupe veľkosti n je taká funkcia T(n), že T(n) = O(g(n)), ale neexistuje c z R také, že limn→∞ T(n)/g(n) = c
  • Preskúmajte princípy a základné myšlienky samovyvažovacích binárnych vyhľadávacích stromov.