#include #include #include #include #include #define SIZE 100 // Struktura na vytvorenie bynarneho stromu. Ma ulozenu hodnotu, pole charov, a referencii na l'aveho a praveho syna. // V danom pripade je to plny bynarny strom, cize kazdy uzol bude mat dva alebo nullu synov struct tree{ char value[SIZE]; /// Referencii v pamati struct tree* left; struct tree* right; }; // Funkcia na uvolnenie bynarneho stromu. Je to recursivna implementacia, znamena, ze danu funkciu budeme volat vo vnuri seba na iny argument. // Tato funkcia a vsetky ine recursivne funkcii v kontexte binarneho stromu operuju na baze postorder, cize budeme navstevat' na zaciatku podstromy a potom sam uzol // Pre-order operuje tak, ze na zaciatku ide sam uzol a potom podstormy, a inorder - jeden podstrom, uzol iny podstrom void destroy_tree(struct tree* tree){ /// V danom pripade, uvolnime l'avy podstrom, potom pravy podstrom a koren if(tree->left){ // dojde do uzla bez synov, ho uvolnime a uvolnime vsetky ine uzly v podstrome destroy_tree(tree->left); } if(tree->right){ // Tak tiez, prechadzame do uzla bez potomkov pravom podstrome destroy_tree(tree->right); } //Vlasne uvolnime v pamati uzol free(tree); } // Funkcia pre nacitanie stromu struct tree* read_tree(){ ///Buffer pre nacitanie char buffer[SIZE]; /// Inticializacia vsetkych bytov na 0 memset(buffer,0,SIZE); /// vlasne nacitanie jedneho riadku. Ako argumenty mame kam, pocet bytov a odkial' char* r = fgets(buffer,SIZE,stdin); // Vratime NULL ak nepodarilo nacitat' if(!r) { return NULL; } /// Odmietnime newline a premenime ho na null terminator - koniec riadku buffer[strcspn(buffer, "\n")] = '\0'; /// Allokujeme v pamati jeden uzol. Calloc inticializuje vsetky byty na 0. struct tree* node = calloc(1,sizeof(struct tree)); ///Prekopirujeme naticany riadok v pamati do hodnoty uzla memcpy(node->value,buffer,SIZE); // Ak ma riadok *, tak to znamena ze to odpoved - listovy uzol a nebude mat' synov if(buffer[0] == '*'){ // Syny su NULL node->left = NULL; node->right = NULL; // vratime uzol return node; } // Pokracujeme v nacitani, ak nie je to odpoved node->left = read_tree(); node->right = read_tree(); /// Ak sconcime nacitanie, tak vratime koren return node; } /// Pomocna funkcia na vyhl'adovanie nepravidelneho vstupu bool find_the_incorrect(struct tree* tree) { if (tree == NULL) return false; if /// Ak strcspn, funkcia ze najdi kde prvy krat tento pattern, vrati index, tak to znamena, ze tieto chary su v slove // Ak symboly nie su v ret'azce, tak to vratilo by dlzku retazca // Overenie pre nekoretne otazky: nemozu mat' * na zacatku ((strcspn(tree->value, "*") != strlen(tree->value) && strcspn(tree->value, "?") != strlen(tree->value)) ) { // vratime true ak to je nekorektna otakzka return true; } // prechadzame po vsetkemu stromu a vyhladavame recursivne prvky // || - logicky operator OR - alebo // Na zacatku pozrieme do l'aveho podstromu a potom do praveho return find_the_incorrect(tree->left) || find_the_incorrect(tree->right); } void print_tree(struct tree* tree,int offset){ for (int i = 0; i < offset; i++){ printf(" "); } printf("%s",tree->value); if (tree->left){ print_tree(tree->left,offset +3); print_tree(tree->right,offset +3); } } // Funkcia pre pocitanie listov binarneho stromu. Recursivna implementacia. // List - je to uzol bez synov. Musi ich byt na 1 viac ako vnutornych uzlov, tych, co maju potomkov. int count_leaves(struct tree* tree){ // Na zacatku pocet je null int count = 0; if(tree == NULL){ // Ak koren je NULL, tak to znamena ze strom je NULL a vratime pocet, cize 0 return count; } // Inak, ak je len jeden koren - vratime 1 if(tree->left == NULL && tree->right == NULL && tree){ return count +1; } // Inak prechadzame strom, na zaciatku volame funkciu na l'avy podstom a potom a pravy else{ return count + count_leaves(tree->left) + count_leaves(tree->right); } } /// Tak tiez, recursivna funkcia na pocet ne lisov int count_non_leaves(struct tree* tree){ // Ak je uzol NULL alebo ne ma synov, tak vratime 0 if(tree == NULL || ( tree->left== NULL && tree->right ==NULL)){ return 0; } // Inak, vratime 1(sam uzol) + pocet ne uzlov v oboch podstromoch return 1 + count_non_leaves(tree->left) +count_non_leaves(tree->right); } /// Main pre systemem int main(void){ /// Overenie na medzeru po databaze bool whitespace = false; /// Vytvorime strom struct tree* tree = read_tree(); /// Buffer pre prazdny riadok char line[SIZE]; /// Spocitame listy a vnutorny uzly int numOfLeaves = count_leaves(tree); int numOfNonLeaves = count_non_leaves(tree); /// Pole pre odpoved' od pouzivatel'a char answer[SIZE]; // Hl'adame prazdny riadok pomcou while while (fgets(line, sizeof(line), stdin)) { // Pokial' mame co nacitovat' po databazu pre strom /// Musime odmietnut newline aby spravne porovnat' line[strcspn(line, "\n")] = '\0'; /// Ak prvy prvok bufferu bude null terminator, tak to bude prazdny riadok if (line[0] == '\0') { // Check je splneny whitespace = true; // Vyjdeme z cyklusu break; } } /// Prechadzame podstromy a hl'adame nespravne vstupy bool found = find_the_incorrect(tree); printf("Expert z bufetu to vie.\n"); /// Ak nepodarilo nacitst alebo nie je tam medzera alebo nasli sme nespravny vstup, vypiseme hlasku a sconcime program. // To plati ak zaciatok bazy nie je spravny /// Ak stringy su rovnake, atak strcmp vrati 0 if(!tree || !whitespace || found || strcmp(tree->value, "Rastie to nad zemou alebo pod zemou ?") == 0){ printf("Chybna databaza\n"); return 0; } printf("Pozna %d druhov ovocia a zeleniny.\n", numOfLeaves); printf("Odpovedajte 'a' pre prvu moznost alebo 'n' pre druhu moznost."); /// Pokial uzol nie je null, pokracuejme v systeme while(tree){ /// Vypis otazky printf("\n%s", tree->value); /// Nacitame vstup od pouzivatel'a fgets(answer, sizeof(answer), stdin); // Odmietnime newline pre porovnanie answer[strcspn(answer, "\n")] = '\0'; // Ak je to prazdny ridok, tak to znamena ze vstup uz ne mame if(answer[0] == '\0'){ printf("\nKoniec vstupu\n"); return 0; } /// Ak je to nie, tak prejdeme na pravy podstrom if(strcmp(answer, "n") == 0){ tree = tree->right; /// Ak hodnotra daneho uzla je odpoved, ju vypiseme a skoncime prgram if(tree->value[0] == '*'){ printf("\n%s\n", tree->value); printf("Koniec\n"); return 0; } } // Analogicky pre ano else if(strcmp(answer, "a") == 0){ tree = tree->left; if(tree->value[0] == '*'){ printf("\n%s\n", tree->value); printf("Koniec\n"); return 0; } } /// Inak, fakticke skoncime program else{ printf("\nNerozumiem\n"); return 0; } } /// Uvolnime v pamati strom'"" destroy_tree(tree); return 0;}