├── .gitignore ├── .vscode ├── c_cpp_properties.json └── settings.json ├── README.md ├── additional01 ├── 01-shunting-yard.cpp ├── 02-peak-list.cpp ├── 03-intervals.cpp ├── 04-predicate.cpp ├── 05-concatenate.cpp ├── 06-all-in-one.cpp ├── 07-reduce.cpp ├── 08-equalize.cpp ├── 09-shuffle.cpp ├── 12-zero-sums.cpp └── README.md ├── media ├── sem01 │ └── sem01-stream-hierarchy.png ├── sem02 │ ├── sem02-ds-classification.png │ ├── sem02-merge-function.png │ ├── sem02-merge-sort-steps.png │ └── sem02-stack.png ├── sem03 │ ├── sem03-christmas-presents.png │ ├── sem03-pop.png │ └── sem03-push.png ├── sem04 │ ├── sem04-binary.png │ ├── sem04-circle-game.png │ ├── sem04-circular-queue-array.png │ ├── sem04-circular-queue.png │ ├── sem04-islands.png │ ├── sem04-maze.png │ └── sem04-queue.png ├── sem05 │ ├── sem05-linked-lists.png │ └── sem05-one-two-list.png ├── sem07 │ ├── sem07-binary-tree-terminology.png │ └── sem07-binary-tree.png ├── sem08 │ ├── sem08-bst-delete-2-children.png │ ├── sem08-bst-delete-leaf-1-child.png │ ├── sem08-bst-example.png │ ├── sem08-bst-insert.png │ ├── sem08-bst-search.png │ ├── sem08-bst.png │ ├── sem08-degenerate-bst.png │ └── sem08-invalid-bst-exmple.png ├── sem09 │ ├── sem09-avl-tree.png │ ├── sem09-left-left-case.png │ ├── sem09-left-right-case.png │ ├── sem09-right-left-case.png │ ├── sem09-right-right-case.png │ ├── sem09-right-rotation.png │ ├── sem09-rotation.png │ └── sem09-same-bst-example.png ├── sem10 │ ├── oriented-graph.png │ └── unoriented-graph.png ├── sem11 │ ├── sem11-max-heap.png │ └── sem11-min-heap.png ├── sem12 │ ├── sem12-connected-components.png │ ├── sem12-dijkstra-algorithm.png │ ├── sem12-dijkstra.png │ └── sem12-topological-sort.png └── sem15 │ ├── sem15-chaining.png │ └── sem15-hash-basic.png ├── sem01 ├── README.md ├── basic-sorts.cpp ├── complexity.cpp ├── flattened.txt ├── sorted-file.txt ├── text-file.cpp ├── unflattened.txt └── war-and-peace-chapter1.txt ├── sem02 ├── README.md ├── array-stack.hpp ├── main.cpp └── sorts.cpp ├── sem03 ├── README.md ├── linked-stack.hpp └── main.cpp ├── sem04 ├── README.md ├── chess-knight-problem.cpp ├── circular-queue.hpp ├── linked-queue.cpp ├── n-binary-numbers.cpp └── shortest-path-in-maze.cpp ├── sem05 ├── 01-detect-cycle.cpp ├── 02-nth-element-backwards.cpp ├── 03-remove-duplicates.cpp ├── 05-merge-sort-doubly-linked-list.cpp ├── README.md ├── doubly-linked-list.cpp └── singly-linked-list.cpp ├── sem06 ├── 01-merge.cpp ├── 02-insert.cpp ├── 03-rearrange.cpp ├── 04-reverse-in-groups.cpp └── README.md ├── sem07 ├── README.md └── main.cpp ├── sem08 ├── README.md ├── bst.cpp └── isBST.cpp ├── sem09 ├── 01-kth-smallest-and-largest.cpp ├── 02-gca.cpp ├── 03-sameBST.cpp ├── README.md └── avl.cpp ├── sem10 ├── README.md ├── adjacency-list-graph │ ├── adjacency-list-graph.cpp │ └── adjacency-list-graph.hpp ├── adjacency-matrix-graph │ ├── adjacency-matrix-graph.cpp │ └── adjacency-matrix-graph.hpp ├── algorithms.cpp ├── algorithms.hpp ├── edge-list-graph │ ├── edge-list-graph.cpp │ └── edge-list-graph.hpp ├── graph.cpp ├── graph.hpp └── main.cpp ├── sem11 ├── README.md └── priority-queue.cpp ├── sem12 ├── README.md ├── algorithms.cpp ├── algorithms.hpp └── main.cpp ├── sem13 ├── README.md ├── binary-tree-paths.cpp ├── left-look-same-as-right-look.cpp └── left-right-look.cpp ├── sem14 ├── README.md ├── graph problems │ ├── connected-components-average.cpp │ └── sums-of-even-odd-distances.cpp └── tree-problems.cpp └── sem15 ├── README.md └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Foldres 2 | K2 3 | D2 4 | test 5 | github-tasks 6 | 7 | # Prerequisites 8 | *.d 9 | 10 | # Compiled Object files 11 | *.slo 12 | *.lo 13 | *.o 14 | *.obj 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Compiled Dynamic libraries 21 | *.so 22 | *.dylib 23 | *.dll 24 | 25 | # Fortran module files 26 | *.mod 27 | *.smod 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/gcc", 10 | "cStandard": "gnu17", 11 | "cppStandard": "gnu++20", 12 | "intelliSenseMode": "linux-gcc-x64" 13 | } 14 | ], 15 | "version": 4 16 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.tcc": "cpp", 4 | "array": "cpp", 5 | "atomic": "cpp", 6 | "bit": "cpp", 7 | "cctype": "cpp", 8 | "clocale": "cpp", 9 | "cmath": "cpp", 10 | "cstdarg": "cpp", 11 | "cstddef": "cpp", 12 | "cstdint": "cpp", 13 | "cstdio": "cpp", 14 | "cstdlib": "cpp", 15 | "cwchar": "cpp", 16 | "cwctype": "cpp", 17 | "deque": "cpp", 18 | "unordered_map": "cpp", 19 | "vector": "cpp", 20 | "exception": "cpp", 21 | "algorithm": "cpp", 22 | "functional": "cpp", 23 | "iterator": "cpp", 24 | "memory": "cpp", 25 | "memory_resource": "cpp", 26 | "numeric": "cpp", 27 | "optional": "cpp", 28 | "random": "cpp", 29 | "string": "cpp", 30 | "string_view": "cpp", 31 | "system_error": "cpp", 32 | "tuple": "cpp", 33 | "type_traits": "cpp", 34 | "utility": "cpp", 35 | "fstream": "cpp", 36 | "initializer_list": "cpp", 37 | "iosfwd": "cpp", 38 | "iostream": "cpp", 39 | "istream": "cpp", 40 | "limits": "cpp", 41 | "new": "cpp", 42 | "ostream": "cpp", 43 | "sstream": "cpp", 44 | "stdexcept": "cpp", 45 | "streambuf": "cpp", 46 | "typeinfo": "cpp", 47 | "list": "cpp", 48 | "any": "cpp", 49 | "bitset": "cpp", 50 | "cfenv": "cpp", 51 | "charconv": "cpp", 52 | "chrono": "cpp", 53 | "cinttypes": "cpp", 54 | "codecvt": "cpp", 55 | "complex": "cpp", 56 | "condition_variable": "cpp", 57 | "csetjmp": "cpp", 58 | "csignal": "cpp", 59 | "cstring": "cpp", 60 | "ctime": "cpp", 61 | "cuchar": "cpp", 62 | "forward_list": "cpp", 63 | "map": "cpp", 64 | "set": "cpp", 65 | "unordered_set": "cpp", 66 | "ratio": "cpp", 67 | "regex": "cpp", 68 | "future": "cpp", 69 | "iomanip": "cpp", 70 | "mutex": "cpp", 71 | "scoped_allocator": "cpp", 72 | "shared_mutex": "cpp", 73 | "thread": "cpp", 74 | "typeindex": "cpp", 75 | "valarray": "cpp", 76 | "variant": "cpp", 77 | "stack": "cpp", 78 | "climits": "cpp" 79 | } 80 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Семинар СДП, група 8 2 | 3 | ## :books: Упражнения 4 | | № | Тема | 5 | | --- | --- | 6 | | 1. | [Файлове - преговор. Анализ на алгоритми - преговор. Сортиране (част 1).](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem01) | 7 | | 2. | [Сортиране (част 2). Увод в структурите от данни. Реализация на стек с масив.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem02) | 8 | | 3. | [Реализация на стек с последователно свързване. Задачи върху стек.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem03) | 9 | | 4. | [Опашка. Задачи върху опашка.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem04) | 10 | | 5. | [Свързан списък с една връзка. Свързан списък с две връзки. Итератор.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem05) | 11 | | 6. | [Задачи върху свързани списъци.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem06) | 12 | | 7. | [Двоично дърво.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem07) | 13 | | 8. | [Двоично наредено дърво.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem08) | 14 | | 9. | [Балансирано двоично наредено дърво.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem09) | 15 | | 10. | [Графи.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem10) | 16 | | 11. | [Приоритетна опашка.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem11) | 17 | | 12. | [Алгоритми върху графи.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem12) | 18 | | 13. | [Задачи върху дървета и графи (част 1).](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem13) | 19 | | 14. | [Задачи върху дървета и графи (част 2).](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem14) | 20 | | 15. | [Хеш таблици.](https://github.com/ivanahristova/data-structures-fmi/tree/main/sem15) | 21 | 22 | 23 | ## :movie_camera: [Връзка към записи от упражнения - част 1](https://drive.google.com/drive/u/1/folders/1d1aYBa-TETQqbbLsCifGSN5F8b0pwQSr) 24 | 25 | ## :movie_camera: [Връзка към записи от упражнения - част 2](https://drive.google.com/drive/u/3/folders/1iggNcAFK7A3QTECvx3eyGNiKi2HLWjMQ) 26 | 27 | ## :movie_camera: [Връзка към запис от допълнително упражнение](https://drive.google.com/drive/u/1/folders/13OMPx-bWNmxEjNbGy102ipH2_8gaY_v_) -------------------------------------------------------------------------------- /additional01/01-shunting-yard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | Нека имаме 2 стека: numbers и operators 7 | 8 | 1. Докато имаме символи в израза - прочитаме следващия символ: 9 | 1.1. Ако е число – добавяме стойността му в numbers 10 | 1.2. Ако е оператор – докато operators не е празен и приоритета на операцията на върха му е по-висок или равен на сегашната: 11 | 1.2.1. Взимаме оператора operators 12 | 1.2.2. Взимаме две стойности numbers 13 | 1.2.3. Прилагаме оператора 14 | 1.2.4. Добавяме резултатата в стека 15 | 1.3. Ако е лява скоба – добавяме я в operators 16 | 1.4. Ако е дясна скоба - докато на върха на operators нямаме лява скоба: 17 | 1.4.1. Взимаме оператора на върха на operators 18 | 1.4.2. Взимаме 2 операнди от numbers 19 | 1.4.3. Прилагаме оператора 20 | 1.4.4. Добавяме получения резултат в numbers 21 | 22 | 2. Докато operators не е празен: 23 | 2.1. Взимаме операцията на върха на operators 24 | 2.2. Взимаме две стойности от numbers 25 | 2.3. Добавяме резултата от прилагането на операцията в numbers 26 | 27 | 3. Последната стойност в стека е резултата 28 | 29 | */ 30 | 31 | bool isDigit(char c) 32 | { 33 | return c >= '0' && c <= '9'; 34 | } 35 | 36 | bool isOperator(char c) 37 | { 38 | return c == '+' || c == '-' || c == '*' || c == '/'; 39 | } 40 | 41 | int getPrecedence(char op) 42 | { 43 | switch (op) 44 | { 45 | case '-': return 1; 46 | case '+': return 1; 47 | case '*': return 2; 48 | case '/': return 2; 49 | default: return 0; 50 | } 51 | } 52 | 53 | int getOperationResult(int left, int right, char op) 54 | { 55 | switch (op) 56 | { 57 | case '-': return left - right; 58 | case '+': return left + right; 59 | case '*': return left * right; 60 | case '/': return left / right; 61 | default: return 0; 62 | } 63 | } 64 | 65 | void evaluateCurrent(std::stack& numbers, std::stack& operators) 66 | { 67 | int right = numbers.top(); 68 | numbers.pop(); 69 | 70 | int left = numbers.top(); 71 | numbers.pop(); 72 | 73 | char op = operators.top(); 74 | operators.pop(); 75 | 76 | numbers.push(getOperationResult(left, right, op)); 77 | } 78 | 79 | int evaluate(std::string expr) 80 | { 81 | std::stack numbers; 82 | std::stack operators; 83 | 84 | int exprLength = expr.length(); 85 | 86 | for (int i = 0; i < exprLength; i++) 87 | { 88 | if (isDigit(expr[i])) 89 | { 90 | int number = 0; 91 | 92 | while (i < exprLength && isDigit(expr[i])) 93 | { 94 | number *= 10; 95 | number += (int)expr[i] - '0'; 96 | i++; 97 | } 98 | 99 | numbers.push(number); 100 | i--; 101 | } 102 | else if (isOperator(expr[i])) 103 | { 104 | while (!operators.empty() && getPrecedence(operators.top()) >= getPrecedence(expr[i])) 105 | { 106 | evaluateCurrent(numbers, operators); 107 | } 108 | 109 | operators.push(expr[i]); 110 | } 111 | else if (expr[i] == '(') 112 | { 113 | operators.push(expr[i]); 114 | } 115 | else if (expr[i] == ')') 116 | { 117 | while (!operators.empty() && operators.top() != '(') 118 | { 119 | evaluateCurrent(numbers, operators); 120 | } 121 | 122 | if (!operators.empty()) 123 | { 124 | operators.pop(); 125 | } 126 | } 127 | } 128 | 129 | while (!operators.empty()) 130 | { 131 | evaluateCurrent(numbers, operators); 132 | } 133 | 134 | return numbers.top(); 135 | } 136 | 137 | 138 | int main() 139 | { 140 | std::cout << evaluate("20+1*6") << '\n'; 141 | std::cout << evaluate("15*2+2") << '\n'; 142 | std::cout << evaluate("2*(2+12)") << '\n'; 143 | std::cout << evaluate("3*(14*14+5)/14") << '\n'; 144 | } 145 | -------------------------------------------------------------------------------- /additional01/02-peak-list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct SNode 4 | { 5 | int data; 6 | SNode* next; 7 | 8 | SNode(int data, SNode* next = nullptr) 9 | : data(data), next(next) {} 10 | }; 11 | 12 | struct DNode 13 | { 14 | SNode* data; 15 | DNode* prev; 16 | DNode* next; 17 | 18 | DNode(SNode* data, DNode* prev = nullptr, DNode* next = nullptr) 19 | : data(data), prev(prev), next(next) {} 20 | }; 21 | 22 | int getListLength(DNode* list) 23 | { 24 | int length = 0; 25 | 26 | while (list) 27 | { 28 | length++; 29 | list = list->next; 30 | } 31 | 32 | return length; 33 | } 34 | 35 | int getNumber(SNode* list) 36 | { 37 | int number = 0; 38 | 39 | while (list) 40 | { 41 | number *= 10; 42 | number += list->data; 43 | list = list->next; 44 | } 45 | 46 | return number; 47 | } 48 | 49 | bool isValid(DNode* list) 50 | { 51 | int half = getListLength(list) / 2; 52 | 53 | DNode* iter = list; 54 | 55 | int counter = 0; 56 | while (++counter < half) 57 | { 58 | if (getNumber(iter->data) > getNumber(iter->next->data)) 59 | { 60 | return false; 61 | } 62 | 63 | iter = iter->next; 64 | } 65 | 66 | iter = iter->next; 67 | 68 | counter = 0; 69 | while (++counter < half) 70 | { 71 | if (getNumber(iter->data) < getNumber(iter->next->data)) 72 | { 73 | return false; 74 | } 75 | 76 | iter = iter->next; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | void free(SNode* list) 83 | { 84 | while (list) 85 | { 86 | SNode* toDelete = list; 87 | list = list->next; 88 | delete toDelete; 89 | } 90 | } 91 | 92 | void free(DNode* list) 93 | { 94 | while (list) 95 | { 96 | DNode* toDelete = list; 97 | free(list->data); 98 | list = list->next; 99 | delete toDelete; 100 | } 101 | } 102 | 103 | int main() 104 | { 105 | SNode* n11 = new SNode(1); 106 | SNode* n12 = new SNode(0); 107 | SNode* n13 = new SNode(0); 108 | 109 | n11->next = n12; 110 | n12->next = n13; 111 | 112 | SNode* n21 = new SNode(1); 113 | SNode* n22 = new SNode(0); 114 | SNode* n23 = new SNode(0); 115 | 116 | n21->next = n22; 117 | n22->next = n23; 118 | 119 | SNode* n31 = new SNode(3); 120 | SNode* n32 = new SNode(8); 121 | SNode* n33 = new SNode(1); 122 | 123 | n31->next = n32; 124 | n32->next = n33; 125 | 126 | SNode* n41 = new SNode(4); 127 | SNode* n42 = new SNode(8); 128 | SNode* n43 = new SNode(1); 129 | 130 | n41->next = n42; 131 | n42->next = n43; 132 | 133 | SNode* n51 = new SNode(3); 134 | SNode* n52 = new SNode(3); 135 | SNode* n53 = new SNode(3); 136 | 137 | n51->next = n52; 138 | n52->next = n53; 139 | 140 | SNode* n61 = new SNode(2); 141 | SNode* n62 = new SNode(8); 142 | SNode* n63 = new SNode(1); 143 | 144 | n61->next = n62; 145 | n62->next = n63; 146 | 147 | DNode* n1 = new DNode(n11, nullptr); 148 | DNode* n2 = new DNode(n21, n1); 149 | DNode* n3 = new DNode(n31, n2); 150 | DNode* n4 = new DNode(n41, n3); 151 | DNode* n5 = new DNode(n51, n4); 152 | DNode* n6 = new DNode(n61, n6); 153 | 154 | n1->next = n2; 155 | n2->next = n3; 156 | n3->next = n4; 157 | n4->next = n5; 158 | n5->next = n6; 159 | 160 | std::cout << isValid(n1); 161 | 162 | free(n1); 163 | } -------------------------------------------------------------------------------- /additional01/03-intervals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct DNode 4 | { 5 | int data; 6 | DNode* prev; 7 | DNode* next; 8 | 9 | DNode(int data, DNode* prev = nullptr, DNode* next = nullptr) 10 | : data(data), prev(prev), next(next) {} 11 | }; 12 | 13 | struct SNode 14 | { 15 | DNode* data; 16 | SNode* next; 17 | 18 | SNode(DNode* data, SNode* next = nullptr) 19 | : data(data), next(next) {} 20 | }; 21 | 22 | DNode* getSecondHalf(DNode* list) 23 | { 24 | DNode* slow = list; 25 | DNode* fast = list; 26 | 27 | while (fast->next && fast->next->next) 28 | { 29 | slow = slow->next; 30 | fast = fast->next->next; 31 | } 32 | 33 | DNode* secondHalf = slow->next; 34 | slow->next = nullptr; 35 | return secondHalf; 36 | } 37 | 38 | DNode* merge(DNode* first, DNode* second) 39 | { 40 | if (!first) 41 | { 42 | return second; 43 | } 44 | 45 | if (!second) 46 | { 47 | return first; 48 | } 49 | 50 | if (first->data <= second->data) 51 | { 52 | first->next = merge(first->next, second); 53 | first->next->prev = first; 54 | first->prev = nullptr; 55 | return first; 56 | } 57 | else 58 | { 59 | second->next = merge(first, second->next); 60 | second->next->prev = second; 61 | second->prev = nullptr; 62 | return second; 63 | } 64 | } 65 | 66 | DNode* mergeSort(DNode* head) 67 | { 68 | if (head == nullptr || head->next == nullptr) 69 | { 70 | return head; 71 | } 72 | 73 | DNode* secondHalf = getSecondHalf(head); 74 | head = mergeSort(head); 75 | secondHalf = mergeSort(secondHalf); 76 | 77 | return merge(head, secondHalf); 78 | } 79 | 80 | struct Interval 81 | { 82 | int low; 83 | int high; 84 | 85 | Interval(int low, int high) 86 | : low(low), high(high) {} 87 | }; 88 | 89 | Interval getInterval(DNode* list) 90 | { 91 | Interval interval(list->data, 0); 92 | 93 | while (list->next) 94 | { 95 | list = list->next; 96 | } 97 | 98 | interval.high = list->data; 99 | return interval; 100 | } 101 | 102 | bool intersect(Interval i1, Interval i2) 103 | { 104 | return !(i1.high < i2.low || i2.high < i1.low); 105 | } 106 | 107 | void filter(SNode* list) 108 | { 109 | SNode* iter = list; 110 | 111 | while (iter && iter->next) 112 | { 113 | Interval int1 = getInterval(iter->data); 114 | Interval int2 = getInterval(iter->next->data); 115 | 116 | if (intersect(int1, int2)) 117 | { 118 | iter->next = iter->next->next; 119 | } 120 | else 121 | { 122 | iter = iter->next; 123 | } 124 | } 125 | } 126 | 127 | void print(DNode* head) 128 | { 129 | if (head == nullptr) 130 | { 131 | std::cout << "[]"; 132 | return; 133 | } 134 | 135 | DNode* iter = head; 136 | std::cout << "["; 137 | while (iter->next) 138 | { 139 | std::cout << iter->data << "->"; 140 | iter = iter->next; 141 | } 142 | std::cout << iter->data << "]"; 143 | } 144 | 145 | void print(SNode* head) 146 | { 147 | if (head == nullptr) 148 | { 149 | std::cout << "{}"; 150 | return; 151 | } 152 | 153 | SNode* iter = head; 154 | std::cout << "{"; 155 | while (iter->next) 156 | { 157 | print(iter->data); 158 | std::cout << " <-> "; 159 | iter = iter->next; 160 | } 161 | print(iter->data); 162 | std::cout << "}"; 163 | } 164 | 165 | void sort(SNode* list) 166 | { 167 | SNode* iter = list; 168 | 169 | while (iter) 170 | { 171 | DNode* sorted = mergeSort(iter->data); 172 | iter->data = sorted; 173 | iter = iter->next; 174 | } 175 | } 176 | 177 | int main() 178 | { 179 | // SNode* l1 = new SNode(10, new SNode(1, new SNode(2, new SNode(3, new SNode(6, new SNode(-3, new SNode(-3, new SNode(-3, new SNode(-3, nullptr))))))))); 180 | // SNode* l2 = new SNode(12, new SNode(10, new SNode(-10, new SNode(10, nullptr)))); 181 | // SNode* l3 = new SNode(1, new SNode(2, new SNode(2, new SNode(3, new SNode(6, new SNode(8, new SNode(5, new SNode(-3, new SNode(3, nullptr))))))))); 182 | 183 | // DNode* n1 = new DNode(l1); 184 | // DNode* n2 = new DNode(l2, n1); 185 | // DNode* n3 = new DNode(l3, n2); 186 | 187 | // n1->next = n2; 188 | // n2->next = n3; 189 | 190 | DNode* n1 = new DNode(3); 191 | DNode* n2 = new DNode(5, n1); 192 | DNode* n3 = new DNode(1, n2); 193 | DNode* n4 = new DNode(7, n3); 194 | DNode* n5 = new DNode(4, n4); 195 | 196 | n1->next = n2; 197 | n2->next = n3; 198 | n3->next = n4; 199 | n4->next = n5; 200 | 201 | DNode* n6 = new DNode(4); 202 | DNode* n7 = new DNode(7, n6); 203 | DNode* n8 = new DNode(1, n7); 204 | 205 | n6->next = n7; 206 | n7->next = n8; 207 | 208 | DNode* n9 = new DNode(5); 209 | DNode* n10 = new DNode(3, n9); 210 | 211 | n9->next = n10; 212 | 213 | DNode* n11 = new DNode(10); 214 | DNode* n12 = new DNode(12, n11); 215 | 216 | n11->next = n12; 217 | 218 | SNode* l1 = new SNode(n1); 219 | SNode* l2 = new SNode(n6); 220 | SNode* l3 = new SNode(n9); 221 | SNode* l4 = new SNode(n11); 222 | 223 | l1->next = l2; 224 | l2->next = l3; 225 | l3->next = l4; 226 | 227 | sort(l1); 228 | print(l1); 229 | 230 | std::cout << '\n'; 231 | 232 | filter(l1); 233 | print(l1); 234 | } -------------------------------------------------------------------------------- /additional01/04-predicate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Node 4 | { 5 | int data; 6 | Node* next; 7 | 8 | Node(int data, Node* next = nullptr) 9 | : data(data), next(next) {} 10 | }; 11 | 12 | Node* filter(Node* head, bool (*pred)(int)) 13 | { 14 | Node* newHead = head; 15 | Node* iter = head; 16 | Node* end = head; 17 | Node* initialEnd = head; 18 | Node* prev = nullptr; 19 | 20 | while (end->next) 21 | { 22 | end = end->next; 23 | } 24 | 25 | initialEnd = end; 26 | 27 | while (iter != initialEnd) 28 | { 29 | if(!pred(iter->data)) 30 | { 31 | Node* newEnd = iter; 32 | 33 | if (prev) 34 | { 35 | prev->next = iter->next; 36 | } 37 | else 38 | { 39 | newHead = iter->next; 40 | } 41 | 42 | iter = iter->next; 43 | newEnd->next = nullptr; 44 | end->next = newEnd; 45 | end = newEnd; 46 | } 47 | else 48 | { 49 | prev = iter; 50 | iter = iter->next; 51 | } 52 | } 53 | 54 | return newHead; 55 | } 56 | 57 | bool isEven(int data) 58 | { 59 | return data % 2 == 0; 60 | } 61 | 62 | void print(Node* head) 63 | { 64 | while (head) 65 | { 66 | std::cout << head->data << ' '; 67 | head = head->next; 68 | } 69 | } 70 | 71 | int main() 72 | { 73 | Node* n5 = new Node(5); 74 | Node* n4 = new Node(4, n5); 75 | Node* n3 = new Node(3, n4); 76 | Node* n2 = new Node(2, n3); 77 | Node* n1 = new Node(1, n2); 78 | 79 | Node* head = filter(n1, &isEven); 80 | print(head); 81 | } -------------------------------------------------------------------------------- /additional01/05-concatenate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | struct Node 7 | { 8 | std::queue data; 9 | Node* next; 10 | 11 | Node(std::queue data, Node* next = nullptr) 12 | : data(data), next(next) {} 13 | 14 | }; 15 | 16 | template 17 | bool validate(std::queue q, bool (*pred)(T)) 18 | { 19 | while (!q.empty() && pred(q.front())) 20 | { 21 | q.pop(); 22 | } 23 | 24 | return q.empty(); 25 | } 26 | 27 | template 28 | void concatenateQueues(std::queue& q1, std::queue& q2) 29 | { 30 | while (!q2.empty()) 31 | { 32 | q1.push(q2.front()); 33 | q2.pop(); 34 | } 35 | } 36 | 37 | template 38 | void concatenate(Node* head, bool (*pred)(T)) 39 | { 40 | Node* iter = head; 41 | 42 | std::vector validation; 43 | 44 | while (iter) 45 | { 46 | validation.push_back(validate(iter->data, pred)); 47 | iter = iter->next; 48 | } 49 | 50 | int index1 = 0; 51 | int index2 = 1; 52 | 53 | iter = head; 54 | while (iter && iter->next) 55 | { 56 | if (validation[index1] && validation[index2]) 57 | { 58 | concatenateQueues(iter->data, iter->next->data); 59 | iter->next = iter->next->next; 60 | index2++; 61 | } 62 | else 63 | { 64 | index1++; 65 | index2++; 66 | iter = iter->next; 67 | } 68 | } 69 | } 70 | 71 | bool isUpperCase(char c) 72 | { 73 | return c >= 'A' && c <= 'Z'; 74 | } 75 | 76 | template 77 | void printQueue(std::queue q) 78 | { 79 | while (!q.empty()) 80 | { 81 | std::cout << q.front() << ' '; 82 | q.pop(); 83 | } 84 | } 85 | 86 | void print(Node* head) 87 | { 88 | Node* iter = head; 89 | 90 | while (iter->next) 91 | { 92 | printQueue(iter->data); 93 | std::cout << " -> "; 94 | iter = iter->next; 95 | } 96 | printQueue(iter->data); 97 | } 98 | 99 | int main() 100 | { 101 | std::queue q1({'a', 'b', 'C', 'D'}); 102 | std::queue q2({'A', 'B'}); 103 | std::queue q3({'C', 'D'}); 104 | std::queue q4({'E', 'F', 'G'}); 105 | std::queue q5({'H', 'h', 'I'}); 106 | 107 | Node* n5 = new Node(q5); 108 | Node* n4 = new Node(q4, n5); 109 | Node* n3 = new Node(q3, n4); 110 | Node* n2 = new Node(q2, n3); 111 | Node* n1 = new Node(q1, n2); 112 | 113 | print(n1); 114 | std::cout << '\n'; 115 | concatenate(n1, &isUpperCase); 116 | print(n1); 117 | } -------------------------------------------------------------------------------- /additional01/06-all-in-one.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Node 6 | { 7 | int data; 8 | Node* next; 9 | 10 | Node(int data, Node* next = nullptr) 11 | : data(data), next(next) {} 12 | }; 13 | 14 | struct SNode 15 | { 16 | std::stack st; 17 | SNode* prev; 18 | SNode* next; 19 | 20 | SNode(std::stack st, SNode* prev = nullptr, SNode* next = nullptr) 21 | : st(st), prev(prev), next(next) {} 22 | 23 | }; 24 | 25 | struct QNode 26 | { 27 | std::queue q; 28 | QNode* prev; 29 | QNode* next; 30 | 31 | QNode(std::queue q, QNode* prev = nullptr, QNode* next = nullptr) 32 | : q(q), prev(prev), next(next) {} 33 | 34 | }; 35 | 36 | struct DNode 37 | { 38 | Node* l; 39 | DNode* prev; 40 | DNode* next; 41 | 42 | DNode(Node* data, DNode* prev = nullptr, DNode* next = nullptr) 43 | : l(data), prev(prev), next(next) {} 44 | }; 45 | 46 | 47 | DNode* intersection(SNode* stackList, QNode* queueList) 48 | { 49 | DNode* start = nullptr; 50 | DNode* end = nullptr; 51 | 52 | while (stackList && queueList) 53 | { 54 | Node* newIntersection = nullptr; 55 | Node* endIntersection = nullptr; 56 | 57 | while (!stackList->st.empty() && !queueList->q.empty()) 58 | { 59 | if (stackList->st.top() < queueList->q.front()) 60 | { 61 | queueList->q.pop(); 62 | } 63 | else if (stackList->st.top() > queueList->q.front()) 64 | { 65 | stackList->st.pop(); 66 | } 67 | else 68 | { 69 | Node* newNode = new Node(stackList->st.top()); 70 | 71 | if (!newIntersection) 72 | { 73 | newIntersection = newNode; 74 | endIntersection = newNode; 75 | } 76 | else 77 | { 78 | endIntersection->next = newNode; 79 | endIntersection = newNode; 80 | } 81 | 82 | stackList->st.pop(); 83 | queueList->q.pop(); 84 | } 85 | } 86 | 87 | DNode* newNode = new DNode(newIntersection); 88 | 89 | if (!start) 90 | { 91 | start = newNode; 92 | end = newNode; 93 | } 94 | else if (start == end) 95 | { 96 | start->next = newNode; 97 | newNode->prev = start; 98 | end = newNode; 99 | } 100 | else 101 | { 102 | newNode->prev = end; 103 | end->next = newNode; 104 | end = newNode; 105 | } 106 | 107 | stackList = stackList->next; 108 | queueList = queueList->next; 109 | } 110 | 111 | return start; 112 | } 113 | 114 | void print(Node* head) 115 | { 116 | if (head == nullptr) 117 | { 118 | std::cout << "[]"; 119 | return; 120 | } 121 | 122 | Node* iter = head; 123 | std::cout << "["; 124 | while (iter->next) 125 | { 126 | std::cout << iter->data << "->"; 127 | iter = iter->next; 128 | } 129 | std::cout << iter->data << "]"; 130 | } 131 | 132 | void print(DNode* head) 133 | { 134 | if (head == nullptr) 135 | { 136 | std::cout << "{}"; 137 | return; 138 | } 139 | 140 | DNode* iter = head; 141 | std::cout << "{"; 142 | while (iter->next) 143 | { 144 | print(iter->l); 145 | std::cout << " <-> "; 146 | iter = iter->next; 147 | } 148 | print(iter->l); 149 | std::cout << "}"; 150 | } 151 | 152 | int main() 153 | { 154 | std::stack s1({1, 4, 5}); 155 | std::stack s2({6, 7, 8, 9}); 156 | std::stack s3({10, 11, 12}); 157 | 158 | SNode* n1 = new SNode(s1); 159 | SNode* n2 = new SNode(s2, n1); 160 | SNode* n3 = new SNode(s3, n2); 161 | 162 | n1->next = n2; 163 | n2->next = n3; 164 | 165 | std::queue q1({4, 1}); 166 | std::queue q2({14, 11, 10, 9, 8, 7, 6}); 167 | std::queue q3({12, 5}); 168 | 169 | QNode* m1 = new QNode(q1); 170 | QNode* m2 = new QNode(q2, m1); 171 | QNode* m3 = new QNode(q3, m2); 172 | 173 | m1->next = m2; 174 | m2->next = m3; 175 | 176 | DNode* head = intersection(n1, m1); 177 | print(head); 178 | } -------------------------------------------------------------------------------- /additional01/07-reduce.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | Node* next; 9 | 10 | Node(T data, Node* next = nullptr) 11 | : data(data), next(next) {} 12 | }; 13 | 14 | template 15 | T reduce(Node* head, T (*F)(const T&, const T&)) 16 | { 17 | if (!head) 18 | { 19 | throw std::runtime_error("Reduce called on an empty list"); 20 | } 21 | 22 | if (!head->next) 23 | { 24 | return head->data; 25 | } 26 | 27 | head->data = F(head->data, head->next->data); 28 | Node* toDelete = head->next; 29 | head->next = head->next->next; 30 | delete toDelete; 31 | return reduce(head, F); 32 | } 33 | 34 | int plus(const int& a, const int& b) 35 | { 36 | return a + b; 37 | } 38 | 39 | int div(const int& a, const int& b) 40 | { 41 | return a / b; 42 | } 43 | 44 | template 45 | void free(Node* list) 46 | { 47 | while (list) 48 | { 49 | Node* toDelete = list; 50 | list = list->next; 51 | delete toDelete; 52 | } 53 | } 54 | 55 | int main() 56 | { 57 | // Node* n5 = new Node(3); 58 | // Node* n4 = new Node(2, n5); 59 | // Node* n3 = new Node(1, n4); 60 | // Node* n2 = new Node(0, n3); 61 | // Node* n1 = new Node(10, n2); 62 | 63 | // std::cout << reduce(n1, &plus); 64 | 65 | // free(n1); 66 | 67 | Node* n14 = new Node(2); 68 | Node* n13 = new Node(4, n14); 69 | Node* n12 = new Node(16, n13); 70 | Node* n11 = new Node(1024, n12); 71 | 72 | std::cout << reduce(n11, &div); 73 | 74 | free(n11); 75 | } -------------------------------------------------------------------------------- /additional01/09-shuffle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Нека е даден списък L с N елемента. Да се дефинира подходящо параметризирана функция shuffle, 5 | която получава адреса на първия елемент на списъка. Функцията да пренарежда възлите на списъка така, 6 | че елементите от втората половина на списъка да се преместят в началото на списъка, но в обратен ред 7 | (при нечетен брой елементи считаме средния елемент за принадлежащ към първата половина на списъка). 8 | 9 | L1 -> L2 -> L3 -> L4 -> L5 ➡ L5 -> L4 -> L1 -> L2 -> L3 10 | 11 | При решението на задачата да не се изтриват или заделят нови възли, 12 | а да се използват съществуващите. Могат да се използват други изучени структури от данни. 13 | */ 14 | 15 | template 16 | struct Node 17 | { 18 | T data; 19 | Node* next; 20 | 21 | Node(T data, Node* next = nullptr) 22 | : data(data), next(next) {} 23 | }; 24 | 25 | template 26 | Node* getSecondHalf(Node* list) 27 | { 28 | Node* slow = list; 29 | Node* fast = list; 30 | 31 | while (fast->next && fast->next->next) 32 | { 33 | slow = slow->next; 34 | fast = fast->next->next; 35 | } 36 | 37 | Node* secondHalf = slow->next; 38 | slow->next = nullptr; 39 | return secondHalf; 40 | } 41 | 42 | template 43 | Node* reverse(Node* head) 44 | { 45 | Node* prev = nullptr; 46 | Node* current = head; 47 | 48 | while (current) 49 | { 50 | Node* next = current->next; 51 | current->next = prev; 52 | prev = current; 53 | current = next; 54 | } 55 | 56 | head = prev; 57 | return head; 58 | } 59 | 60 | template 61 | Node* shuffle(Node* head) 62 | { 63 | Node* secondHalf = getSecondHalf(head); 64 | Node* end = secondHalf; 65 | secondHalf = reverse(secondHalf); 66 | end->next = head; 67 | return secondHalf; 68 | } 69 | 70 | void print(Node* head) 71 | { 72 | while (head) 73 | { 74 | std::cout << head->data << ' '; 75 | head = head->next; 76 | } 77 | } 78 | 79 | int main() 80 | { 81 | Node* head = new Node(1, new Node(2, new Node(3, new Node(4, new Node(5))))); 82 | Node* head2 = new Node(1,new Node(2, new Node(3, new Node(4, new Node(5, 83 | new Node(6, new Node(7, new Node(8, new Node(9, new Node(10)))))))))); 84 | 85 | 86 | Node* newHead = shuffle(head); 87 | print(newHead); 88 | 89 | Node* newHead2 = shuffle(head2); 90 | std::cout << '\n'; 91 | print(newHead2); 92 | } -------------------------------------------------------------------------------- /additional01/12-zero-sums.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Node 5 | { 6 | int data; 7 | Node* next; 8 | 9 | Node(int data, Node* next) 10 | : data(data), next(next){} 11 | }; 12 | 13 | Node* filterZeroSums(Node* head) 14 | { 15 | Node* iter = head; 16 | 17 | std::stack st; 18 | 19 | while (iter != nullptr) 20 | { 21 | std::stack copy = st; 22 | int currentSum = iter->data; 23 | 24 | bool deleted = false; 25 | 26 | while (!copy.empty() && !deleted) 27 | { 28 | Node* temp = copy.top(); 29 | copy.pop(); 30 | currentSum += temp->data; 31 | 32 | if (currentSum == 0) 33 | { 34 | deleted = true; 35 | 36 | if (temp == head) 37 | { 38 | head = iter->next; 39 | iter = head; 40 | } 41 | else 42 | { 43 | copy.top()->next = iter->next; 44 | iter = iter->next; 45 | } 46 | 47 | int deletedCount = 0; 48 | while (temp != iter) 49 | { 50 | Node* toDelete = temp; 51 | temp = temp->next; 52 | deletedCount++; 53 | delete toDelete; 54 | } 55 | 56 | while (--deletedCount > 0) 57 | { 58 | st.pop(); 59 | } 60 | } 61 | 62 | } 63 | 64 | if (!deleted) 65 | { 66 | st.push(iter); 67 | iter = iter->next; 68 | } 69 | } 70 | 71 | return head; 72 | } 73 | 74 | void print(Node* list) 75 | { 76 | while (list != nullptr) 77 | { 78 | std::cout << list->data << ' '; 79 | list = list->next; 80 | } 81 | } 82 | 83 | int main() 84 | { 85 | Node* list1 = new Node(99, new Node(3, new Node(-3, new Node(6, new Node(-10, new Node(4, new Node(8, new Node(6, new Node(2, new Node(-8, nullptr)))))))))); 86 | Node* list2 = new Node(10, new Node(1, new Node(2, new Node(3, new Node(6, new Node(-3, new Node(-3, new Node(-3, new Node(-3, nullptr))))))))); 87 | 88 | print(filterZeroSums(list1)); 89 | std::cout << '\n'; 90 | print(filterZeroSums(list2)); 91 | } -------------------------------------------------------------------------------- /additional01/README.md: -------------------------------------------------------------------------------- 1 | # Подготовка за първо контролно 2 | 3 | ### **Задача 1.** 4 | Да се напише функция, която намира стойността на израз в инфиксен запис. Приемаме, че изразът е валиден, т.е. скобите са балансирани и операциите са добре дефинирани.
5 | Множество от операции: {+, -, *, /}. 6 | 7 | ### **Задача 2.** 8 | Даден е свързан списък с две връзки L. Елементите на списъка са свързани списъци с една връзка l_{1}, ..., l_{n} с елементи числа в интервала [0, 9]. Всеки от елементите на L представя число (3->4->5 е числото 345). 9 | Да се напише функция, която проверява дали числата от първата половина на списъка образуват растяща редица, а числата от втората половина на списъка - намаляваща.
10 | Равенствата са нестроги.
11 | Списъкът L има четен брой възли.
12 | Всеки от възлите на списък L образува валидно число.
13 | 14 | ### **Задача 3.** 15 | Даден е свързан списък с една връзка L. Елементите на L са свързани списъци с две връзки l_{1}, ..., l_{n} с елементи цели числа.
16 | 17 | а. Да се напише функция, която пренарежда елементите на l_{1}, ..., l_{n}, така че всеки от тях да образува нарастваща редица.
18 | б. Нека имаме интервал I = [min{l_i}, max{l_i}]. Да се напише функция, която филтрира списъците l_{1}, ..., l_{n}, премахвайки списъците l_{i}, чиито интервал I се застъпва с интервала на списък l_{i-1} (интервал I-1).
19 | 20 | ### **Задача 4.** 21 | Даден е свързан списък с една връзка L.
22 | Да се напише функция, която пренарежда елементите на списъка така, че елементите, които отговарят на даден предикат p да бъдат преди останалите.
23 | 24 | ### **Задача 5.** 25 | Даден е свързан списък с една връзка L, чийто елементи са опашки.
26 | Да се напише функция, което конкатенира всички последователни опашки, чиито елементи отговарят на даден предикат p.
27 | 28 | ### **Задача 6.** 29 | Дадени са два свързани списъка с две връзки L1 и L2. Те са с една и съща дължина.
30 | Първият е с елементи сортитани стекове от цели числа, а вторият е с елементи обратно сортирани опашки от цели числа.
31 | Да се генерира свързан списък с две връзки от сеченията на елементите на стек si и опашка qi.
32 | Сечението да се представя чрез свързан списък с една връзка.
33 | 34 | ## Задачи от контролни и изпити 35 | 36 | ### **Задача 7.** 37 | Нека е даден следният шаблон на структура:
38 | 39 | ```cpp 40 | template 41 | struct Node {T data; Node *next;}; 42 | ``` 43 | 44 | Да се реализира функция *reduce*, която приема два параметъра: указател към първия елемент на линеен едносвързан списък L с възли от тип Node и двуместна **функция** F от тип (const T&, const T&) -> T. Резултатът от изпълнението на *reduce* да е стойността на приложението на ляво-асоциативния оператор F последователно над елементите на L, или F(...F(F(l_1, l_2), l_3) ..., l_k), където l_1,...,l_k са елементите на списъка L.
45 | 46 | При K = 0 да се генерира подходяща грешка (изключение), а при K = 1 стойността на функцията да е l_1.
47 | 48 | Пример: 49 | Нека имаме списъка L с елементи 1024, 16, 4, 2. Нека d(x,y) = x/y. Тогава резултатът от reduce (L,d) ще бъде 8, тъй като div(div(div(1024, 16), 4), 2) = 8 50 | 51 | ### **Задача 8.** 52 | Нека е даден списък L с елементи стекове. Възлите на L са от тип Node> (или друга готова реализация на стек, с която разполагате). Да се дефинира подходящо параметризирана функция equalize(L), която размества елементите на стековете така, че да няма два стека в L с разлика в броя на елементите, по-голяма от 1. 53 | 54 | ### **Задача 9.** 55 | Нека е даден списък L с N елемента. Да се дефинира подходящо параметризирана функция shuffle, която получава адреса на първия елемент на списъка. Функцията да пренарежда възлите на списъка така, че елементите от втората половина на списъка да се преместят в началото на списъка, но в обратен ред (при нечетен брой елементи считаме средния елемент за принадлежащ към първата половина на списъка). 56 | 57 | L1 -> L2 -> L3 -> L4 -> L5 ➡ L5 -> L4 -> L1 -> L2 -> L3 58 | 59 | При решението на задачата да не се изтриват или заделят нови възли, а да се използват съществуващите. Могат да се използват други изучени структури от данни. 60 | 61 | ### **Задача 10.** 62 | Напишете функция, която получава като аргументи два свързани списъка от числа и връща като резултат списък, съставен от сечението на техните елементи, подредени в нарастващ ред. Самите списъци – аргументи да не се променят. Демонстрирайте използването на функцията в кратка програма. 63 | 64 | ## За упражнение: 65 | 66 | ### **Задача 11.** 67 | Да се напише функция, която проверява дали свързан списък с една връзка е палиндром. 68 | 69 | ### **Задача 12.** 70 | Реализирайте функция, която приема свързан списък и премахва всяка последователност от поредни върхове, чиято сума на елементите е 0. 71 | 72 | Пример: 73 | Вход: 99 3 -3 6 -10 4 8 6 2 -8 74 | Изход: 99 8 75 | 76 | ### **Задача 13.** 77 | Реализирайте функциите:
78 | а) ```filter```
79 | б) ```map```
80 | за свързани списъци с една връзка 81 | 82 | ### **Задача 14.** 83 | Дефинираме израз по следния начин: ( <операнд> <операция> <операнд> ). 84 | Операндите представляват малки латински букви. 85 | Операциите са елементи на множеството {‘+’, ‘-’, ‘*’}. 86 | 87 | а) Да се напише функция, която получава израз като символен низ и проверява дали е валиден, следвайки следните правила: 88 | 1. Всяка буква е валиден израз 89 | 2. Скоби има само около израз от вида <операнд> <операция> <операнд> 90 |
91 | 92 | Примери: 93 | a е валиден израз 94 | (a+b) е валиден израз 95 | (a) не е валиден израз 96 | a+b не е валиден израз 97 | ((b-c)) не е валиден израз 98 | (a+(a*c)) е валиден израз 99 | 100 | б) Напишете функция, която получава израз от същия вид и изкарва позициите на невалидните скоби, ако има такива 101 | 102 | Примери: 103 | ((a+b)) -- позиции 0 и 6 104 | (а) -- позиции 0 и 2 105 | a+b -- няма невалидни скоби 106 | 107 | ### **Задача 15.** 108 | Напишете функция, която пренарежда свързан списък по зададен начин в линейно време. 109 | -------------------------------------------------------------------------------- /media/sem01/sem01-stream-hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem01/sem01-stream-hierarchy.png -------------------------------------------------------------------------------- /media/sem02/sem02-ds-classification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem02/sem02-ds-classification.png -------------------------------------------------------------------------------- /media/sem02/sem02-merge-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem02/sem02-merge-function.png -------------------------------------------------------------------------------- /media/sem02/sem02-merge-sort-steps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem02/sem02-merge-sort-steps.png -------------------------------------------------------------------------------- /media/sem02/sem02-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem02/sem02-stack.png -------------------------------------------------------------------------------- /media/sem03/sem03-christmas-presents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem03/sem03-christmas-presents.png -------------------------------------------------------------------------------- /media/sem03/sem03-pop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem03/sem03-pop.png -------------------------------------------------------------------------------- /media/sem03/sem03-push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem03/sem03-push.png -------------------------------------------------------------------------------- /media/sem04/sem04-binary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-binary.png -------------------------------------------------------------------------------- /media/sem04/sem04-circle-game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-circle-game.png -------------------------------------------------------------------------------- /media/sem04/sem04-circular-queue-array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-circular-queue-array.png -------------------------------------------------------------------------------- /media/sem04/sem04-circular-queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-circular-queue.png -------------------------------------------------------------------------------- /media/sem04/sem04-islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-islands.png -------------------------------------------------------------------------------- /media/sem04/sem04-maze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-maze.png -------------------------------------------------------------------------------- /media/sem04/sem04-queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem04/sem04-queue.png -------------------------------------------------------------------------------- /media/sem05/sem05-linked-lists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem05/sem05-linked-lists.png -------------------------------------------------------------------------------- /media/sem05/sem05-one-two-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem05/sem05-one-two-list.png -------------------------------------------------------------------------------- /media/sem07/sem07-binary-tree-terminology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem07/sem07-binary-tree-terminology.png -------------------------------------------------------------------------------- /media/sem07/sem07-binary-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem07/sem07-binary-tree.png -------------------------------------------------------------------------------- /media/sem08/sem08-bst-delete-2-children.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-bst-delete-2-children.png -------------------------------------------------------------------------------- /media/sem08/sem08-bst-delete-leaf-1-child.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-bst-delete-leaf-1-child.png -------------------------------------------------------------------------------- /media/sem08/sem08-bst-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-bst-example.png -------------------------------------------------------------------------------- /media/sem08/sem08-bst-insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-bst-insert.png -------------------------------------------------------------------------------- /media/sem08/sem08-bst-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-bst-search.png -------------------------------------------------------------------------------- /media/sem08/sem08-bst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-bst.png -------------------------------------------------------------------------------- /media/sem08/sem08-degenerate-bst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-degenerate-bst.png -------------------------------------------------------------------------------- /media/sem08/sem08-invalid-bst-exmple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem08/sem08-invalid-bst-exmple.png -------------------------------------------------------------------------------- /media/sem09/sem09-avl-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-avl-tree.png -------------------------------------------------------------------------------- /media/sem09/sem09-left-left-case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-left-left-case.png -------------------------------------------------------------------------------- /media/sem09/sem09-left-right-case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-left-right-case.png -------------------------------------------------------------------------------- /media/sem09/sem09-right-left-case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-right-left-case.png -------------------------------------------------------------------------------- /media/sem09/sem09-right-right-case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-right-right-case.png -------------------------------------------------------------------------------- /media/sem09/sem09-right-rotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-right-rotation.png -------------------------------------------------------------------------------- /media/sem09/sem09-rotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-rotation.png -------------------------------------------------------------------------------- /media/sem09/sem09-same-bst-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem09/sem09-same-bst-example.png -------------------------------------------------------------------------------- /media/sem10/oriented-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem10/oriented-graph.png -------------------------------------------------------------------------------- /media/sem10/unoriented-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem10/unoriented-graph.png -------------------------------------------------------------------------------- /media/sem11/sem11-max-heap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem11/sem11-max-heap.png -------------------------------------------------------------------------------- /media/sem11/sem11-min-heap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem11/sem11-min-heap.png -------------------------------------------------------------------------------- /media/sem12/sem12-connected-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem12/sem12-connected-components.png -------------------------------------------------------------------------------- /media/sem12/sem12-dijkstra-algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem12/sem12-dijkstra-algorithm.png -------------------------------------------------------------------------------- /media/sem12/sem12-dijkstra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem12/sem12-dijkstra.png -------------------------------------------------------------------------------- /media/sem12/sem12-topological-sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem12/sem12-topological-sort.png -------------------------------------------------------------------------------- /media/sem15/sem15-chaining.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem15/sem15-chaining.png -------------------------------------------------------------------------------- /media/sem15/sem15-hash-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivanahristova/data-structures-fmi/0c014655657f0e6ff4a14ac861044ea7b84b0129/media/sem15/sem15-hash-basic.png -------------------------------------------------------------------------------- /sem01/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 1 2 | # Файлове (преговор). Анализ на алгоритми (преговор). Сортиране (част 1). 3 | 4 | ## I. Файлове 5 | 6 | **1. Йерархия на потоците в C++** 7 | 8 |   9 | 10 | ![Hierarchy of C++ stream classes](../media/sem01/sem01-stream-hierarchy.png) 11 | 12 | **2. Работа с файлове** 13 | 14 | За да работим с файлови потоци, трябва да включим хедъра `fstream`: 15 | ```cpp 16 | #include 17 | ``` 18 | 19 | Преди работа с даден файл той трябва да бъде отворен, а след като работата с него приключи - затворен. 20 | 21 | ```cpp 22 | open(const char* filename [, int mode] [, int prot]); 23 | ``` 24 | 25 | **3. Режими на отваряне** 26 | 27 | | Флаг | Значение | 28 | | --- | --- | 29 | | **`ios::in`** | Отваряне с операции за вход | 30 | | **`ios::out`** | Отваряне с операции за изход | 31 | | **`ios::binary`** | Отваряне в двоичен режим | 32 | | **`ios::ate`** | **При отваряне** поставя указателя в края на файла | 33 | | **`ios::app`** | **Всички** изходни операции се извършват на края на файла, добавяйки към досегашните данни | 34 | | **`ios::trunc`** | При отваряне за изходни операции за съществуващ файл досегашните данни се изтриват | 35 | 36 | Флаговете се комбинират с побитово OR ('|'). 37 | 38 | 🔴 Важно 39 | - `ios::in` флагът е винаги вдигнат при отваряне на поток за вход 40 | - `ios::out` флагът е винаги вдигнат при отваряне на поток за изход 41 | 42 | **4. Четене и писане** 43 | - текстов 44 | ```cpp 45 | int x; 46 | infile >> x; 47 | int y = 345; 48 | outfile << y; 49 | ``` 50 | 51 | - двоичен 52 | ```cpp 53 | istream& read(char* buffer, streamsize n); 54 | ostream& write(const char* buffer, streamsize n); 55 | ``` 56 | 57 | Задача 1. Да се напише програма за извличане на информация от текст, която поддържа следните функции: 58 | - :pencil2: принтира първите n реда от файл; 59 | - принтира последните n реда от файл; 60 | - принтира всички редове от текста, в които се среща подадена дума; 61 | - :pencil2: сортира файл по редове; 62 | - :pencil2: премахва повтарящите се редове в сортиран файл. 63 | 64 | ## II. Анализ на алгоритми 65 | 66 | **1. Цели при създаване на алгоритъм** 67 | - Простота 68 | - Коректност 69 | - Бързодействие 70 | 71 | Как се оценява бързодействието? Чрез изследване на асимптотиката. 72 | *Сложност* наричаме скоростта на нарастването на времето и паметта, необходими за изпълнението на даден алгоритъм, с нарастването на входните данни. 73 | Ще означаваме размера на входните данни с **n**. 74 | 75 | - Сложност по време - как расте времето за изпълнение при нарастване на входа 76 | - Сложност по памет - как расте необходимата памет при нарастване на входа 77 | 78 | **2. Нотации** 79 | - *O*(Big-O): 80 | *O(f)* - множеството от функции, които растат не по-бързо от *f* 81 | - *Ω*(Big-Omega): 82 | *Ω(f)* - множеството от функции, които растат не по-бавно от *f* 83 | - *Θ*(Theta): 84 | *Θ(f)* - множеството от функции, които растат със скоростта на *f* 85 | 86 | **3. Свойства** 87 | 88 | Нека c > 0 - константа. Тогава: 89 | - *O(n + c)* = *O(n)* --- Адитивните константи се игнорират 90 | - *O(c.n)* = *O(n)* --- Мултипликативните константи се игнорират 91 | - Сложността на един алгоритъм се определя от тази функция, която расте най-бързо 92 | 93 | **4. Да се определи сложността на [функциите](https://github.com/IvanaHristova/data-structures-fmi/tree/main/sem01/complexity.cpp)** 94 | 95 | ## III. Сортиране 96 | 97 | **1. Bubble Sort** 98 | 99 | Познат като *метод на мехурчето*. 100 | Работи на принципа на директни сравнения. Масивът се обхожда като се разменят съседни елементи, ако са в инверсия. 101 | 102 | Въпроси: 103 | 1. Какво се случва след първото обхождане на масива? 104 | 2. Колко пъти трябва да бъде обходен масива, за да бъде сортиран? 105 | 3. Стабилен ли е алгоритъмът? 106 | 107 | Предимства: 108 | 1. Има свойството да проверява дали масивът е сортиран ефикасно 109 | 2. Добър за учебна цел 110 | 111 | **2. Selection Sort** 112 | 113 | Познат като *метод на пряката селекция*. 114 | Алгоритъмът сортира масива чрез последователни обхождания и търсене на най-малък елемент. Този елемент се разменя с елемента на *правилната* му позиция. 115 | 116 | Предимства: 117 | 1. Прави минимален брой swap-ове: n - 1 (WC) 118 | 119 | **3. Insertion Sort** 120 | 121 | Алгоритъмът наподобява начина, по който картоиграч реди картите в ръката си. 122 | Алгоритъмът строи сортиран масив като взима поредния необходен елемент и намира павилното му място измежду сортираните елементи. 123 | Наивната имплементация на алгоритъма използва swap-ове. "Изтънчената" имплементация запазва елемента в отделна променлива и приплъзва елементите на сортирания подмасив надясно до намиране на правилната позиция. След това елементът се поставя на нея. 124 | 125 | Предимства: 126 | 1. Добър при предварително сортиран масив (или почти сортиран) 127 | 2. По-ефикасен от предните два квадратични алгоритъма 128 | 3. Може да сортира масив с приемането му 129 | 130 | **4. Анализ** 131 | 132 | | \* | Bubble sort | Selection sort | Insertion sort | 133 | | --- | --- | --- | --- | 134 | | BC | *O(n)* | *O(n^2)* | *O(n)* | 135 | | AC | *O(n^2)* | *O(n^2)* | *O(n^2)* | 136 | | WC | *O(n^2)* | *O(n^2)* | *O(n^2)* | 137 | | Memory | *O(1)* | *O(1)* | *O(1)* | 138 | | Stability | :heavy_check_mark: | ❌ | :heavy_check_mark: | 139 | -------------------------------------------------------------------------------- /sem01/basic-sorts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void swap(T* first, T* second) 5 | { 6 | T temp = *first; 7 | *first = *second; 8 | *second = temp; 9 | } 10 | 11 | template 12 | void BubbleSort(T* arr, int n) 13 | { 14 | bool isSwapped = true; 15 | for (int i = 0; i < n && isSwapped; ++i) 16 | { 17 | isSwapped = false; 18 | for (int j = 1; j < n; ++j) 19 | { 20 | if (arr[j - 1] > arr[j]) 21 | { 22 | swap(&arr[j-1], &arr[j]); 23 | isSwapped = true; 24 | } 25 | } 26 | } 27 | 28 | } 29 | 30 | template 31 | void SelectionSort(T* arr, int n) 32 | { 33 | for (int i = 0; i < n; ++i) 34 | { 35 | int minIndex = i; 36 | for (int j = i + 1; j < n; ++j) 37 | { 38 | if (arr[j] < arr[minIndex]) 39 | { 40 | minIndex = j; 41 | } 42 | } 43 | if (minIndex != i) 44 | { 45 | swap(&arr[i], &arr[minIndex]); 46 | } 47 | } 48 | } 49 | 50 | template 51 | void InsertionSort(T* arr, int n) 52 | { 53 | for (int i = 0; i < n; ++i) 54 | { 55 | T key = arr[i]; 56 | int index = i - 1; 57 | 58 | while (index >= 0 && arr[index] > key) 59 | { 60 | arr[index + 1] = arr[index]; 61 | index--; 62 | } 63 | 64 | if (index != i - 1) 65 | { 66 | arr[index + 1] = key; 67 | } 68 | } 69 | } 70 | 71 | int main() 72 | { 73 | const int SIZE = 10; 74 | int arr[SIZE] = {23, 8, 17, 14, 19, 44, 33, 12, 0, 8}; 75 | 76 | BubbleSort(arr, SIZE); 77 | // SelectionSort(arr, SIZE); 78 | // InsertionSort(arr, SIZE); 79 | 80 | for (int i = 0; i < SIZE; ++i) 81 | { 82 | std::cout << arr[i] << ' '; 83 | } 84 | std::cout << '\n'; 85 | } -------------------------------------------------------------------------------- /sem01/complexity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int f1(int n) 4 | { 5 | int sum = 0; 6 | for (int i = 0; i <= n; i++) 7 | { 8 | sum += n; 9 | } 10 | return sum; 11 | } 12 | 13 | int f2(int n) 14 | { 15 | int sum = 0; 16 | for (int i = 0; i <= n; i++) 17 | { 18 | for (int j = i; j <= n; j++) 19 | { 20 | sum++; 21 | } 22 | } 23 | return sum; 24 | } 25 | 26 | int f3(int n) 27 | { 28 | int sum = 0; 29 | for (int i = 0; i <= n; i++) 30 | { 31 | for (int j = 0; j <= i; j++) 32 | { 33 | sum++; 34 | } 35 | } 36 | return sum; 37 | } 38 | 39 | int f4(int n) 40 | { 41 | int sum = 0; 42 | for (int i = 1; i <= n; i*=2) 43 | { 44 | for (int j = 1; j <= n; j++) 45 | { 46 | sum++; 47 | } 48 | } 49 | return sum; 50 | } 51 | 52 | int f5(int n) 53 | { 54 | int sum = 0; 55 | for (int i = 1; i <= n; i*=2) 56 | { 57 | for (int j = 1; j <= i; j++) 58 | { 59 | sum++; 60 | } 61 | } 62 | return sum; 63 | } 64 | 65 | int main() 66 | { 67 | 68 | } -------------------------------------------------------------------------------- /sem01/flattened.txt: -------------------------------------------------------------------------------- 1 | apple 2 | ball 3 | baseball 4 | difference 5 | individual 6 | sensitive 7 | warehouse 8 | -------------------------------------------------------------------------------- /sem01/text-file.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // принтира първите n реда от файл 8 | void printHead(std::ifstream& i_stream, int n) 9 | { 10 | int lines = 0; 11 | std::string line; 12 | while(lines < n && std::getline(i_stream, line)) 13 | { 14 | std::cout << line << '\n'; 15 | lines++; 16 | } 17 | } 18 | 19 | // принтиране на последните n реда с начална позиция на пойнтъра в края на файла 20 | void printTailVer1(std::ifstream& i_stream, int n) 21 | { 22 | int lines = 0; 23 | 24 | while(lines < n && i_stream.tellg() > 0) 25 | { 26 | char c; 27 | i_stream.seekg(-2, std::ios::cur); 28 | i_stream.read(&c, 1); 29 | if (c == '\n') 30 | { 31 | lines++; 32 | } 33 | } 34 | 35 | while (!i_stream.eof()) 36 | { 37 | char c; 38 | i_stream.read(&c, sizeof(char)); 39 | std::cout << c; 40 | } 41 | } 42 | 43 | // принтиране на последните n реда с начална позиция на пойнтъра в началото на файла 44 | void printTailVer2(std::ifstream& i_stream, int n) 45 | { 46 | i_stream.seekg(0, i_stream.end); 47 | 48 | int lines = 0; 49 | 50 | while(lines < n && i_stream.tellg() > 0) 51 | { 52 | char c; 53 | i_stream.seekg(-1, std::ios::cur); 54 | if (i_stream.peek() == '\n') 55 | { 56 | lines++; 57 | } 58 | } 59 | 60 | while (!i_stream.eof()) 61 | { 62 | char c; 63 | i_stream.read(&c, sizeof(char)); 64 | std::cout << c; 65 | } 66 | } 67 | 68 | // принтиране на всички редове, които съдържат дадена дума 69 | void printLinesContainingWord(std::ifstream& i_stream, const std::string word) 70 | { 71 | std::string line; 72 | while(std::getline(i_stream, line)) 73 | { 74 | if(line.find(word) != std::string::npos) 75 | { 76 | std::cout << line << '\n'; 77 | } 78 | } 79 | } 80 | 81 | // сортира файл по редове и запазва сортираното съдържание в нов файл 82 | void sort(std::string filename, std::string newFile) 83 | { 84 | std::ifstream i_stream(filename); 85 | if (!i_stream.is_open()) 86 | { 87 | throw std::runtime_error("Error opening file!"); 88 | } 89 | 90 | std::string line; 91 | std::vector lines; 92 | 93 | while(std::getline(i_stream, line)) 94 | { 95 | line += "\n"; 96 | lines.push_back(line); 97 | } 98 | sort(lines.begin(), lines.end()); 99 | i_stream.close(); 100 | 101 | std::ofstream o_stream(newFile, std::ios::trunc); 102 | for(std::vector::iterator it = lines.begin(); it != lines.end(); ++it) 103 | { 104 | o_stream << *it; 105 | } 106 | 107 | o_stream.close(); 108 | } 109 | 110 | // премахва повтарящите се редове в сортиран файл и запазва филтрирания текст в нов файл 111 | void flattenSortedText(std::string filename, std::string newFile) 112 | { 113 | std::ifstream i_stream(filename); 114 | if (!i_stream.is_open()) 115 | { 116 | throw std::runtime_error("Error opening file!"); 117 | } 118 | 119 | std::string line; 120 | std::vector lines; 121 | 122 | while(std::getline(i_stream, line)) 123 | { 124 | line += "\n"; 125 | if (!lines.empty()) 126 | { 127 | if (lines.back() == line) 128 | { 129 | continue; 130 | } 131 | } 132 | lines.push_back(line); 133 | } 134 | 135 | std::ofstream o_stream(newFile, std::ios::trunc); 136 | for(std::vector::iterator it = lines.begin(); it != lines.end(); ++it) 137 | { 138 | o_stream << *it; 139 | } 140 | 141 | o_stream.close(); 142 | } 143 | 144 | int main() 145 | { 146 | // Example 1 147 | std::ifstream i_file("war-and-peace-chapter1.txt"); 148 | if (!i_file.is_open()) 149 | { 150 | throw std::runtime_error("Error opening file!"); 151 | } 152 | printHead(i_file, 10); 153 | 154 | // Example 2 155 | // std::ifstream i_file("war-and-peace-chapter1.txt", std::ios::ate); 156 | // if (!i_file.is_open()) 157 | // { 158 | // throw std::runtime_error("Error opening file!"); 159 | // } 160 | // printTailVer1(i_file, 10); 161 | // i_file.close(); 162 | 163 | // Example 3 164 | // std::ifstream i_file("war-and-peace-chapter1.txt"); 165 | // if (!i_file.is_open()) 166 | // { 167 | // throw std::runtime_error("Error opening file!"); 168 | // } 169 | // printTailVer2(i_file, 10); 170 | // i_file.close(); 171 | 172 | // Example 4 173 | // std::ifstream i_file("war-and-peace-chapter1.txt"); 174 | // if (!i_file.is_open()) 175 | // { 176 | // throw std::runtime_error("Error opening file!"); 177 | // } 178 | // std::string word = "family"; 179 | // printLinesContainingWord(i_file, word); 180 | // i_file.close(); 181 | 182 | // Example 5 183 | // sort("war-and-peace-chapter1.txt", "sorted-file.txt"); 184 | 185 | // Example 6 186 | // flattenSortedText("unflattened.txt", "flattened.txt"); 187 | } -------------------------------------------------------------------------------- /sem01/unflattened.txt: -------------------------------------------------------------------------------- 1 | apple 2 | apple 3 | ball 4 | baseball 5 | difference 6 | individual 7 | individual 8 | sensitive 9 | warehouse -------------------------------------------------------------------------------- /sem02/array-stack.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ARRAY_STACK_HPP 2 | #define ARRAY_STACK_HPP 3 | 4 | #include 5 | 6 | template 7 | class ArrayStack 8 | { 9 | static const int INITIAL_CAPACITY = 16; 10 | T* data; 11 | int size; 12 | int capacity; 13 | 14 | void free(); 15 | void copy(const ArrayStack& other); 16 | void resize(int size); 17 | 18 | public: 19 | ArrayStack(); 20 | ArrayStack(const ArrayStack& other); 21 | ArrayStack& operator=(const ArrayStack& other); 22 | ~ArrayStack(); 23 | 24 | void push(const T& element); 25 | void pop(); 26 | const T& top() const; 27 | 28 | int getSize() const; 29 | bool empty() const; 30 | }; 31 | 32 | template 33 | void ArrayStack::free() 34 | { 35 | delete[] data; 36 | } 37 | 38 | template 39 | void ArrayStack::copy(const ArrayStack& other) 40 | { 41 | this->size = other.size; 42 | this->capacity = other.capacity; 43 | data = new T[other.capacity]; 44 | 45 | for (int i = 0; i < size; i++) 46 | { 47 | data[i] = other.data[i]; 48 | } 49 | } 50 | 51 | template 52 | void ArrayStack::resize(int newCapacity) 53 | { 54 | T* temp = data; 55 | data = new T[newCapacity]; 56 | 57 | for (int i = 0; i < size; i++) 58 | { 59 | data[i] = temp[i]; 60 | } 61 | 62 | capacity = newCapacity; 63 | delete[] temp; 64 | } 65 | 66 | template 67 | ArrayStack::ArrayStack(): size(0), capacity(INITIAL_CAPACITY) 68 | { 69 | data = new T[capacity]; 70 | } 71 | 72 | template 73 | ArrayStack::ArrayStack(const ArrayStack& other) 74 | { 75 | copy(other); 76 | } 77 | 78 | template 79 | ArrayStack& ArrayStack::operator=(const ArrayStack& other) 80 | { 81 | if (this != &other) 82 | { 83 | free(); 84 | copy(other); 85 | } 86 | return this; 87 | } 88 | 89 | template 90 | ArrayStack::~ArrayStack() 91 | { 92 | free(); 93 | } 94 | 95 | template 96 | void ArrayStack::push(const T& element) 97 | { 98 | if (size == capacity) 99 | { 100 | resize(capacity * 2); 101 | } 102 | 103 | data[size++] = element; 104 | } 105 | 106 | template 107 | void ArrayStack::pop() 108 | { 109 | if (empty()) 110 | { 111 | throw std::runtime_error("Pop called on an empty stack!"); 112 | } 113 | 114 | size--; 115 | 116 | if (size * 2 <= capacity && capacity > 1) 117 | { 118 | resize(capacity / 2); 119 | } 120 | } 121 | 122 | template 123 | const T& ArrayStack::top() const 124 | { 125 | if (empty()) 126 | { 127 | throw std::runtime_error("Top called on an empty stack!"); 128 | } 129 | 130 | return data[size - 1]; 131 | } 132 | 133 | template 134 | int ArrayStack::getSize() const 135 | { 136 | return size; 137 | } 138 | 139 | template 140 | bool ArrayStack::empty() const 141 | { 142 | return size == 0; 143 | } 144 | 145 | #endif -------------------------------------------------------------------------------- /sem02/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "array-stack.hpp" 3 | 4 | int main() 5 | { 6 | ArrayStack st; 7 | std::cout << st.empty() << '\n'; 8 | st.push(23); 9 | st.push(11); 10 | st.push(12); 11 | std::cout << st.getSize() << ' ' << st.top() << '\n'; 12 | st.pop(); 13 | std::cout << st.getSize() << ' ' << st.top() << '\n'; 14 | st.pop(); 15 | std::cout << st.getSize() << ' ' << st.top() << '\n'; 16 | st.pop(); 17 | std::cout << st.empty() << '\n'; 18 | } -------------------------------------------------------------------------------- /sem02/sorts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void merge(T* arr1, int n1, T* arr2, int n2) 5 | { 6 | int size = n1 + n2; 7 | T* arr = new T[size]; 8 | int index = 0, index1 = 0, index2 = 0; 9 | 10 | while(index1 < n1 && index2 < n2) 11 | { 12 | if (arr1[index1] <= arr2[index2]) 13 | { 14 | arr[index++] = arr1[index1++]; 15 | } 16 | else 17 | { 18 | arr[index++] = arr2[index2++]; 19 | } 20 | } 21 | 22 | while (index1 < n1) 23 | { 24 | arr[index++] = arr1[index1++]; 25 | } 26 | 27 | while (index2 < n2) 28 | { 29 | arr[index++] = arr2[index2++]; 30 | } 31 | 32 | for (int i = 0; i < size; i++) 33 | { 34 | arr1[i] = arr[i]; 35 | } 36 | 37 | delete[] arr; 38 | } 39 | 40 | template 41 | void MergeSort(T* arr, int n) 42 | { 43 | if (n <= 1) 44 | { 45 | return; 46 | } 47 | 48 | int mid = n / 2; 49 | MergeSort(arr, mid); 50 | MergeSort(arr + mid, (n - mid)); 51 | merge(arr, mid, arr+mid, (n-mid)); 52 | } 53 | 54 | // Updated version of partition 55 | // It uses one index instead of two to move from left to right of the array 56 | template 57 | int partition(T* arr, int n) 58 | { 59 | int pivotId = n / 2; 60 | T pivot = arr[pivotId]; 61 | int left = 0, right = n - 1; 62 | int index = left; 63 | 64 | while (index <= right) 65 | { 66 | if (arr[index] < pivot) 67 | { 68 | std::swap(arr[left++], arr[index++]); 69 | } 70 | else if (arr[index] > pivot) 71 | { 72 | std::swap(arr[index], arr[right--]); 73 | } 74 | else // element is equal to pivot 75 | { 76 | index++; 77 | } 78 | } 79 | 80 | return right; 81 | } 82 | 83 | template 84 | void QuickSort(T* arr, int n) 85 | { 86 | if (n <= 1) 87 | { 88 | return; 89 | } 90 | 91 | int pivotIndex = partition(arr, n); 92 | QuickSort(arr, pivotIndex); 93 | QuickSort(arr + pivotIndex + 1, (n - pivotIndex - 1)); 94 | } 95 | 96 | void CountingSort(int* arr, int n, int lowerBound, int upperBound) 97 | { 98 | // The interval consists of (upperBound - lowerBound + 1) elements! 99 | // When lowerBound = 0: intervalSize = upperBound + 1! 100 | int intervalSize = upperBound - lowerBound + 1; 101 | int* count = new int[intervalSize]; 102 | int* final = new int[n]; 103 | 104 | for (int i = 0; i < n; i++) 105 | { 106 | count[arr[i] - lowerBound]++; 107 | } 108 | 109 | for (int i = 1; i < intervalSize; i++) 110 | { 111 | count[i] += count[i-1]; 112 | } 113 | 114 | for (int i = n - 1; i >= 0; i--) 115 | { 116 | final[count[arr[i] - lowerBound] - 1] = arr[i]; 117 | count[arr[i]]--; 118 | } 119 | 120 | for (int i = 0; i < n; i++) 121 | { 122 | arr[i] = final[i]; 123 | } 124 | 125 | delete[] count; 126 | delete[] final; 127 | } 128 | 129 | int main() 130 | { 131 | const int SIZE = 20; 132 | int arr[SIZE] = {100, 23, 11, 12, 3, 7, 11, 1, 24, 25, 133 | 11, 65, 11, 2, 4, 15, 9, 18, 0, 67}; 134 | 135 | MergeSort(arr, SIZE); 136 | // QuickSort(arr, SIZE); 137 | // CountingSort(arr, SIZE, 0, 100); 138 | 139 | for (int i = 0; i < SIZE; i++) 140 | { 141 | std::cout << arr[i] << ' '; 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /sem03/README.md: -------------------------------------------------------------------------------- 1 | ># Упражнение № 3 2 | # Реализация на стек със свързан списък. Задачи върху стек. 3 | 4 | ## I. Реализация чрез свързан списък 5 | 6 | **1. push** 7 | 8 | ![Linked stack push](../media/sem03/sem03-push.png) 9 | 10 | **2. pop** 11 | 12 | ![Linked stack pop](../media/sem03/sem03-pop.png) 13 | 14 | ## II. Задачи 📝 15 | 16 | ### **Задача 1.** Обръщане на стек 17 | Да се напише метод за обръщане на стек към реализацията му чрез свързан списък. 18 | 19 | Пример: 20 | [1, 8, 5, 20, 1] ➡ [1, 20, 5, 8 ,1] 21 | 22 | ### **Задача 2.** Балансирани скоби 23 | Напишете функция (isBalanced), която по въведен символен низ, състоящ се само от скоби ({,},(,),[,]), проверява дали той е коректен. 24 | 25 | Примери: 26 | [{[]}()] ➡ true 27 | [([](){[()[]]})()] ➡ true 28 | [{}(]) ➡ false 29 | [{({}(]))} ➡ false 30 | 31 | ### **Задача 3.** RPN calculator 32 | Реализирайте калкулатор, който се базира на обратен полски запис (постфиксен запис). Приемаме, че входът е валиден израз. 33 | 34 | Пример: 35 | Израз в инфиксен запис: ((15 / (7 − (1 + 1))) × 3) − (2 + (1 + 1)) 36 | Вход: 15 7 1 1 + - / 3 * 2 1 1 + + - 37 | Изход: 5 38 | 39 | ❓ При вход в инфиксен запис може да се използва следния [алгоритъм](https://en.wikipedia.org/wiki/Shunting-yard_algorithm).
40 | 41 | 42 | ### **Задача 4.** ✏️ Да се напише функция, която обръща елементите на стек (да се използва std::stack) 43 | 44 | ### **Задача 5.** ✏️ Ще получите N инструкции, всяка от които ще бъде една от следните: 45 | 1 x - добавя елемент x към стека 46 | 2 - трие елемента на върха на стека 47 | 3 - принтира максималния елемент на стека 48 | Да се изведе резултата от изпълнението на инструкциите върху празния стек. 49 | 50 | Пример: 51 | 8 (брой операции) 52 | 1 97 (добавя елемент 97) 53 | 2 (трие елемента на върха на стека (97)) 54 | 1 20 (добавя елемент 20) 55 | 2 (трие елемента на върха на стека (20)) 56 | 1 26 (добавя елемент 26) 57 | 3 (принтира максималния елемент на стека (26)) 58 | 1 91 (добавя елемент 91) 59 | 3 (принтира максималния елемент на стека (91)) 60 | ➡ 26 91 61 | 62 | ### **Задача 6.** ✏️ Да се напише функция, която обръща текст (само реда на думите). 63 | 64 | ### **Задача 7.** ✏️ Задачата за Ханойските кули. 65 | 66 | ### **Задача 8.** ✏️ Коледни подаръци 67 | В магазин за играчки решили да раздадат подаръци на децата от детска градина за Коледа.
68 | За целта подготвили N подаръка (по един за всяко дете) и ги номерирали с числата от 1 до N.
69 | В магазина имало три улея подредени като на фигурата.
70 | Първоначално подаръците били наредени в улей 1 в нарастващ ред.
71 | Децата трябвало да получат подаръците си едно по едно от улей 3, спазвайки определен ред.
72 | Подаръците могат да се пренареждат с помощта на следните команди:
73 | 1 - Най-десният подарък от улей 1 се премества като най-ляв в улей 2.
74 | 2 - Най-левият подарък от улей 2 се премества като най-десен в улей 3.
75 | 3 - Най-левият подарък в улей 2 се премества като най-десен в улей 1.
76 | Всеки от трите улея може да събере всички подаръци.
77 | Напишете програма, която намира последователност от операции, чрез която подаръците могат да се преместят от улей 1 в улей 3 в предварително зададен ред.
78 | 79 | ![Christmas presents fig.1](../media/sem03/sem03-christmas-presents.png) 80 | 81 | Вход: 82 | N - брой на подаръците 83 | N на брой числа, представляващи реда, който трябва да бъдат подредени подаръците в улей 3 84 | Изход: 85 | Поредица от команди, с които да бъде направено преместването. 86 | 87 | Примери: 88 | 4 89 | 1 4 2 3 90 | ➡ 1 1 1 1 2 3 3 2 1 1 2 2 91 | 7 92 | 3 1 2 4 7 5 6 93 | ➡ 1 1 1 1 1 2 1 1 2 2 2 3 3 2 1 1 2 2 94 | -------------------------------------------------------------------------------- /sem03/linked-stack.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LINKED_STACK_HPP 2 | #define LINKED_STACK_HPP 3 | 4 | #include 5 | #include 6 | 7 | template 8 | struct Node 9 | { 10 | T data; 11 | Node* next; 12 | 13 | Node(T data) : data(data), next(nullptr) {} 14 | }; 15 | 16 | template 17 | class LinkedStack 18 | { 19 | private: 20 | Node* head; 21 | int size; 22 | 23 | void copy(const LinkedStack& other); 24 | void free(); 25 | void nullify(); 26 | 27 | public: 28 | LinkedStack(); 29 | LinkedStack(const LinkedStack& other); 30 | LinkedStack(LinkedStack&& other) noexcept; 31 | LinkedStack& operator=(const LinkedStack& other); 32 | LinkedStack& operator=(LinkedStack&& other) noexcept; 33 | ~LinkedStack(); 34 | 35 | void push(const T& element); 36 | void pop(); 37 | T& top(); 38 | 39 | int getSize() const; 40 | bool isEmpty() const; 41 | 42 | void reverse(); 43 | void print(); 44 | }; 45 | 46 | template 47 | void LinkedStack::copy(const LinkedStack& other) 48 | { 49 | if (other.isEmpty()) 50 | { 51 | head = nullptr; 52 | size = 0; 53 | return; 54 | } 55 | 56 | Node* iterOther = other.head; 57 | head = new Node(iterOther->data); 58 | iterOther = iterOther->next; 59 | Node* iterThis = head; 60 | 61 | while (iterOther != nullptr) 62 | { 63 | iterThis->next = new Node(iterOther->data); 64 | iterOther = iterOther->next; 65 | iterThis = iterThis->next; 66 | } 67 | 68 | this->size = other.size; 69 | } 70 | 71 | template 72 | void LinkedStack::free() 73 | { 74 | Node* iter = head; 75 | 76 | while (iter != nullptr) 77 | { 78 | Node* toDelete = iter; 79 | iter = iter->next; 80 | delete toDelete; 81 | } 82 | 83 | head = nullptr; 84 | size = 0; 85 | } 86 | 87 | template 88 | void LinkedStack::nullify() 89 | { 90 | head = nullptr; 91 | size = 0; 92 | } 93 | 94 | template 95 | LinkedStack::LinkedStack() : size(0), head(nullptr) {} 96 | 97 | template 98 | LinkedStack::LinkedStack(const LinkedStack& other) 99 | { 100 | copy(other); 101 | } 102 | 103 | template 104 | LinkedStack::LinkedStack(LinkedStack&& rhs) noexcept 105 | : size(std::move(rhs.size)), head(std::move(rhs.head)) 106 | { 107 | rhs.nullify(); 108 | } 109 | 110 | template 111 | LinkedStack& LinkedStack::operator=(const LinkedStack& other) 112 | { 113 | if (this != &other) 114 | { 115 | free(); 116 | copy(other); 117 | } 118 | return *this; 119 | } 120 | 121 | template 122 | LinkedStack& LinkedStack::operator=(LinkedStack&& other) noexcept 123 | { 124 | // assert(this != &other); 125 | 126 | free(); 127 | this->head = std::move(other.head); 128 | this->size = std::move(other.size); 129 | other.nullify(); 130 | 131 | return *this; 132 | } 133 | 134 | template 135 | LinkedStack::~LinkedStack() 136 | { 137 | free(); 138 | } 139 | 140 | template 141 | void LinkedStack::push(const T& element) 142 | { 143 | Node* newNode = new Node(element); 144 | 145 | if (!isEmpty()) 146 | { 147 | newNode->next = head; 148 | } 149 | head = newNode; 150 | size++; 151 | } 152 | 153 | template 154 | void LinkedStack::pop() 155 | { 156 | if (isEmpty()) 157 | { 158 | throw std::runtime_error("Pop called on an empty stack"); 159 | } 160 | 161 | if (head->next == nullptr) 162 | { 163 | delete head; 164 | head = nullptr; 165 | } 166 | else 167 | { 168 | Node* temp = head->next; 169 | delete head; 170 | head = temp; 171 | } 172 | size--; 173 | } 174 | 175 | template 176 | T& LinkedStack::top() 177 | { 178 | if (isEmpty()) 179 | { 180 | throw std::runtime_error("Top called on an empty stack"); 181 | } 182 | 183 | return head->data; 184 | } 185 | 186 | 187 | template 188 | int LinkedStack::getSize() const 189 | { 190 | return size; // O(1) 191 | 192 | // Ако нямаме член-данна size, 193 | // то сложността на метода ще е линейна. 194 | 195 | /* 196 | int counter = 0; 197 | Node* iter = head; 198 | 199 | while (iter != nullptr) 200 | { 201 | counter++; 202 | iter = iter->next; 203 | } 204 | 205 | return counter; 206 | */ 207 | } 208 | 209 | template 210 | bool LinkedStack::isEmpty() const 211 | { 212 | return head == nullptr; 213 | } 214 | 215 | template 216 | void LinkedStack::reverse() 217 | { 218 | if (isEmpty()) 219 | { 220 | return; 221 | } 222 | 223 | Node* iter = head; 224 | Node* nextNode = new Node(iter->data); 225 | iter = iter->next; 226 | 227 | while (iter != nullptr) 228 | { 229 | Node* newNode = new Node(iter->data); 230 | newNode->next = nextNode; 231 | nextNode = newNode; 232 | iter = iter->next; 233 | } 234 | 235 | free(); 236 | head = nextNode; 237 | } 238 | 239 | template 240 | void LinkedStack::print() 241 | { 242 | if (head == nullptr) 243 | { 244 | std::cout << "[]"; 245 | return; 246 | } 247 | 248 | Node* iter = head; 249 | std::cout << '['; 250 | while (iter->next != nullptr) 251 | { 252 | std::cout << iter->data << " -> "; 253 | iter = iter->next; 254 | } 255 | std::cout << iter->data << ']' << '\n'; 256 | } 257 | 258 | #endif 259 | -------------------------------------------------------------------------------- /sem03/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "linked-stack.hpp" 6 | 7 | // ({()[]} 8 | 9 | // st -> ( ? 10 | 11 | // {})))} 12 | // st -> 13 | 14 | // )( 15 | 16 | bool validateParenthesesSymbols(std::string parentheses) 17 | { 18 | int length = parentheses.length(); 19 | bool valid = true; 20 | for (int i = 0; i < length && valid; i++) 21 | { 22 | char current = parentheses[i]; 23 | if (current != '(' && current != ')' && 24 | current != '{' && current != '}' && 25 | current != '[' && current != ']') 26 | { 27 | valid = false; 28 | } 29 | } 30 | return valid; 31 | } 32 | 33 | bool isBalanced(std::string parentheses) 34 | { 35 | if (!validateParenthesesSymbols(parentheses)) 36 | { 37 | return false; 38 | } 39 | 40 | int length = parentheses.length(); 41 | std::stack openingBrackets; 42 | 43 | for (int i = 0; i < length; i++) 44 | { 45 | char curr = parentheses[i]; 46 | 47 | if (curr == '(' || curr == '{' || curr == '[') 48 | { 49 | openingBrackets.push(curr); 50 | continue; 51 | } 52 | if (openingBrackets.empty()) 53 | { 54 | return false; 55 | } 56 | 57 | if ((curr == ')' && openingBrackets.top() != '(') || 58 | (curr == '}' && openingBrackets.top() != '{') || 59 | (curr == ']' && openingBrackets.top() != '[')) 60 | { 61 | return false; 62 | } 63 | 64 | openingBrackets.pop(); 65 | } 66 | return openingBrackets.empty(); 67 | } 68 | 69 | bool isDigit(char c) 70 | { 71 | return c >= '0' && c <= '9'; 72 | } 73 | 74 | void rpnHelper(char c, std::stack& number) 75 | { 76 | double right = number.top(); 77 | number.pop(); 78 | double left = number.top(); 79 | number.pop(); 80 | 81 | switch (c) 82 | { 83 | case '+': number.push(left + right); 84 | break; 85 | case '-': number.push(left - right); 86 | break; 87 | case '*': number.push(left * right); 88 | break; 89 | case '/': number.push(left / right); 90 | break; 91 | 92 | default: 93 | std::invalid_argument("Invalid symbol"); 94 | break; 95 | } 96 | } 97 | 98 | double rpnCalc(std::string input) // valid input in rpn 99 | { 100 | int inputLength = input.length(); 101 | std::stack numbers; 102 | 103 | for (int i = 0; i < inputLength; i++) 104 | { 105 | if (input[i] == ' ') 106 | { 107 | continue; 108 | } 109 | 110 | if (isDigit(input[i])) 111 | { 112 | int number = 0; 113 | 114 | while (i < inputLength && isDigit(input[i])) 115 | { 116 | number *= 10; 117 | number += (int)input[i++] - '0'; 118 | } 119 | numbers.push(number); 120 | } 121 | else 122 | { 123 | rpnHelper(input[i], numbers); 124 | } 125 | } 126 | return numbers.top(); 127 | } 128 | 129 | int main() 130 | { 131 | LinkedStack st1; 132 | st1.push(12); 133 | st1.push(21); 134 | st1.push(11); 135 | LinkedStack st2(st1); 136 | 137 | int st1Size = st1.getSize(); 138 | for (int i = 0; i < st1Size; i++) 139 | { 140 | st1.pop(); 141 | } 142 | 143 | st1.push(33); 144 | st1.push(23); 145 | st1.push(13); 146 | 147 | LinkedStack st3; 148 | st3.push(12); 149 | st3 = st1; 150 | 151 | st3.print(); 152 | 153 | st1.reverse(); 154 | st1.print(); 155 | st2.reverse(); 156 | st2.print(); 157 | 158 | std::cout << "((): " << isBalanced("(()") << '\n' 159 | << "[{[]}()]: " << isBalanced("[{[]}()]") << '\n' 160 | << "[([](){[()[]]})()]: " << isBalanced("[([](){[()[]]})()]") << '\n' 161 | << "[{}(]): " << isBalanced("[{}(])") << '\n' 162 | << "[{({}(]))}: " << isBalanced("[{({}(]))}") << '\n'; 163 | 164 | std::cout << rpnCalc("15 7 1 1 + - / 3 * 2 1 1 + + -") << '\n'; 165 | } -------------------------------------------------------------------------------- /sem04/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 4 2 | # Опашкa. Задачи. 3 | 4 | ## I. Опашка 5 | - линейна динамична структура от данни 6 | - базира се на принципа FIFO (First In First Out) 7 | - отговаря на реалната ни представа за опашка 8 | - началото на опашката наричаме глава (**head**) 9 |

10 | 11 | ![Queue](../media/sem04/sem04-queue.png) 12 | 13 | **Основни операции** 14 | - добавяне на елемент в края на опашката 15 | - триене на елемент от началото на опашката 16 | - достъп до елемента на върха на опашката 17 | 18 | **Приложения** 19 | - Представяне на реална опашка 20 | - BFS:
21 | Основно свойство - елементите се премахват от опашката в ред, определен от отдалечеността им от началния елемент. Това позволява намирането на най-къс път. Процесът по търсене на най-къс път продължава докато: 22 | 1. Бъде достигната дестинацията 23 | 2. Опашката остане празна - не е намерен път 24 | 25 | **Имплементации** 26 | - чрез масив (циклична опашка) 27 | - чрез свързан списък 28 | 29 | **Анализ на сложността** 30 | 31 | | Основна операция | Опашка базирана на масив | Опашка базирана на свързан списък | 32 | | --- | --- | --- | 33 | | Добавяне на елемент (enqueue/push) | O(1) | O(1) | 34 | | Премахване на елемент (dequeue/pop) | O(n) | O(1) | 35 | | Достъп до първия елемент (front) | O(1) | O(1) | 36 | 37 | ## II. Задачи 38 | 39 | ### **Задача 1.** Двоични числа 40 | Да се напише функция, която генерира всички двоични числа от 1 до n, използвайки опашка. 41 |
42 |
43 | 44 | ![Binary](../media/sem04/sem04-binary.png) 45 | 46 | n = 10 ➡ 1 10 11 100 101 110 111 1000 1001 1010 47 | 48 | ### **Задача 2.** ✏️ Патица, патица, гъска... 49 | Ще направим игра по подобие на популярната детска игра "Патица, патица, гъска...". Нека имаме n играчи, подредени в кръг и произволно положително цяло число k, за което е изпълнено, че 0 < k < n. 50 |
51 | Игра:
52 | 1. Намираме k-тия играч (този, който се намира на k-та позиция спрямо текущата по посока на часовниковата стрелка)
53 | 2. Елиминираме играча на тази позиция 54 | 3. Ако играчите са повече от 1, повтаряме стъпките.
55 | 4. Ако играчът е един той печели играта.
56 |
57 | 58 | Всеки играч се представя с името си.
59 | Да се напише функция, която връща победителя от играта по подадени играчи и цяло число k. 60 |
61 | 62 | ![Circle](../media/sem04/sem04-circle-game.png) 63 | 64 | ### **Задача 3.** Най-къс път в лабиринт 65 | Дадена е матрица NxM, състояща се от нули и единици. Тя представя лабиринт, в който през полетата, означени с 1 има път, а тези с 0 са стени. Да се напише функция, която намира дължината на най-късия път от подадена начална до подадена крайна позиция в лабиринта. 66 | 67 | ![Maze](../media/sem04/sem04-maze.png) 68 | 69 | ### **Задача 4.** Chess Knight Problem 70 | Да се намери най-късия път от точка А до точка Б на кон на шахматна дъска (или произволно голяма дъска - NxN). 71 | 72 | ### **Задача 5.** ✏️ Острови 73 | Дадена е двоична матрица NxN, където 0 представя поле Вода, а 1 представя поле Земя. 74 | Приемаме, че един остров представят съседни полета, които се докосват (включително, ако са съседни по диагонал). Да се намери броя на островите в матрицата. 75 | 76 | ![Islands](../media/sem04/sem04-islands.png) 77 | 78 | ## III. Циклична опашка 79 | *(незадължителна тема)* 80 | 81 | ![Islands](../media/sem04/sem04-circular-queue.png) 82 | 83 | Базира се на принципа FIFO.
84 | Особеност: последната позиция (клетка) е свързана с първата, образувайки цикъл. От там идва и името на опашката. (Може да се срещне и като ring buffer).
85 | Примерно приложение: смяна на светлините на светофар. 86 | 87 | ![Islands](../media/sem04/sem04-circular-queue-array.png) 88 | 89 | 90 | ## IV. Приоритетна опашка 91 | *(незадължителна тема)* 92 | -------------------------------------------------------------------------------- /sem04/chess-knight-problem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Position 5 | { 6 | int x; 7 | int y; 8 | 9 | Position(int x, int y) 10 | : x(x), y(y) {} 11 | 12 | bool operator==(const Position& rhs) 13 | { 14 | return x == rhs.x && y== rhs.y; 15 | } 16 | 17 | Position& operator+=(const Position& rhs) 18 | { 19 | x += rhs.x; 20 | y += rhs.y; 21 | return *this; 22 | } 23 | 24 | friend Position operator+(Position lhs, const Position& rhs) 25 | { 26 | lhs += rhs; 27 | return lhs; 28 | } 29 | }; 30 | 31 | struct Square 32 | { 33 | Position pos; 34 | int steps; 35 | 36 | Square(Position pos, int steps) 37 | : pos(pos), steps(steps) {} 38 | }; 39 | 40 | bool isInBounds(Position pos, int n) 41 | { 42 | return pos.x >= 1 && pos.x <= n && 43 | pos.y >= 1 && pos.y <= n; 44 | } 45 | 46 | int stepsKnightProblem(Position knight, Position end, int n) 47 | { 48 | // Валидни движения на коня 49 | int statesCount = 8; 50 | std::vector states{{-2, -1}, {-2, 1}, {-1, 2}, {-1, -2}, 51 | {1, -2}, {1, 2}, {2, -1}, {2, 1}}; 52 | 53 | // Матрица за отбелязване на посетените полета от дъската 54 | bool visited[n + 1][n + 1]; 55 | 56 | // В началото всички полета без началната позиция са непосетени 57 | for (int i = 1; i <= n; i++) 58 | { 59 | for (int j = 1; i <= n; i++) 60 | { 61 | visited[i][j] = false; 62 | } 63 | } 64 | 65 | // Единственото посетено поле е това, от което започваме - 66 | // началната позиция на коня 67 | visited[knight.x][knight.y] = true; 68 | 69 | // Създаваме опашка, в която добавяме всяко непосетено поле, което достигнем 70 | std::queue q; 71 | 72 | // Първото посетено поле е това, от което започваме 73 | // До него сме достигнали с 0 стъпки, затова steps = 0 74 | q.push(Square(knight, 0)); 75 | 76 | // Докато опашката не е празна, можем да намерим (най-къс) път до дестинацията 77 | while (!q.empty()) 78 | { 79 | // Текущото поле е на върха на опашката. 80 | // Това е полето, до която сме стигнали с най-малък брой ходове (стъпки), 81 | // защото полетата се добавят в опашката по ред на достигане. 82 | Square current = q.front(); 83 | 84 | // Идея: Ще обходим всички полета, до които можем да достигнем от текущото. 85 | // С това ще изчерпаме възможностите за това поле, затова го махаме от опашката. 86 | q.pop(); 87 | 88 | // Ако позицията на текущото поле отговаря на позицията, която искаме да достигнем, 89 | // то връщаме броя на стъпките, с които е било достигнато. 90 | if (current.pos == end) 91 | { 92 | return current.steps; 93 | } 94 | 95 | // Финалната позиция не е била текущата. 96 | // Добавяме в опашката всички позиции, които могат да бъдат достигнати 97 | // с една стъпка от текущото поле. 98 | // Валидните стъпки/движения са изброени в states. 99 | for (int i = 0; i < statesCount; i++) 100 | { 101 | // Правим отместването от текущата позиция, което е определено от states[i] 102 | Position pos(current.pos + states[i]); 103 | 104 | // Ако: 105 | // 1. новата позиция е в рамките на дъската и 106 | // 2. не е била вече посетена (т.е. не е бил намерен по-къс път до нея на предна стъпка), 107 | // то отбелязваме позицията като посетена, а полето добавяме в опашката със стъпките, 108 | // с които е било достигнато. 109 | // Това са стъпките, с които е било достигнато текущото поле + 1. 110 | if (isInBounds(pos, n) && !visited[pos.x][pos.y]) 111 | { 112 | visited[pos.x][pos.y] = true; 113 | q.push(Square(pos, current.steps + 1)); 114 | } 115 | } 116 | } 117 | 118 | return -1; 119 | } 120 | 121 | int main() 122 | { 123 | int N = 30; 124 | Position startN(1, 1); 125 | Position endN(30, 30); 126 | std::cout << stepsKnightProblem(startN, endN, N) << std::endl; 127 | 128 | int M = 8; 129 | Position startM(1, 1); 130 | Position endM(8, 8); 131 | std::cout << stepsKnightProblem(startM, endM, M) << std::endl; 132 | 133 | return 0; 134 | } -------------------------------------------------------------------------------- /sem04/circular-queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CIRCULAR_QUEUE_HPP 2 | #define CIRCULAR_QUEUE_HPP 3 | 4 | #include 5 | 6 | template 7 | class CircularQueue 8 | { 9 | private: 10 | 11 | static const int INITIAL_CAPACITY = 8; 12 | T* data; 13 | int count; 14 | int capacity; 15 | int frontIndex; 16 | int backIndex; 17 | 18 | void copy(const CircularQueue& other); 19 | void free(); 20 | void resize(int newCapacity); 21 | 22 | public: 23 | CircularQueue(); 24 | CircularQueue(const CircularQueue& other); 25 | CircularQueue& operator=(const CircularQueue& other); 26 | ~CircularQueue(); 27 | 28 | // Adds element to the back of the queue 29 | void push(const T& value); 30 | 31 | // Removes element from the front of the queue 32 | // Throws exception if queue is empty 33 | void pop(); 34 | 35 | // Returns a constant reference to the first element of the queue 36 | // The first element of a queue is the element at the head (FIFO) 37 | const T& front() const; 38 | 39 | bool empty() const; 40 | int size() const; 41 | }; 42 | 43 | template 44 | CircularQueue::CircularQueue(): count(0), capacity(INITIAL_CAPACITY), frontIndex(0), backIndex(0) { 45 | data = new T[capacity]; 46 | } 47 | 48 | template 49 | void CircularQueue::copy(const CircularQueue& other) { 50 | count = other.count; 51 | capacity = other.capacity; 52 | 53 | frontIndex = 0; 54 | backIndex = count; 55 | 56 | data = new T[capacity]; 57 | for(int i = 0; i < count; i++) { 58 | data[i] = other.data[(i + other.frontIndex) % capacity]; 59 | } 60 | } 61 | 62 | template 63 | CircularQueue::CircularQueue(const CircularQueue& other) { 64 | copy(other); 65 | } 66 | 67 | template 68 | void CircularQueue::free() { 69 | delete[] data; 70 | } 71 | 72 | template 73 | CircularQueue& CircularQueue::operator=(const CircularQueue& other) { 74 | if (this != &other) { 75 | free(); 76 | copy(other); 77 | } 78 | 79 | return *this; 80 | } 81 | 82 | template 83 | CircularQueue::~CircularQueue() { 84 | free(); 85 | } 86 | 87 | template 88 | void CircularQueue::resize(int newCapacity) { 89 | T* const temp = data; 90 | data = new T[newCapacity]; 91 | for(int i = 0; i < count; i++) { 92 | data[i] = temp[(i + frontIndex) % capacity]; 93 | } 94 | 95 | capacity = newCapacity; 96 | frontIndex = 0; 97 | backIndex = count; 98 | 99 | delete temp; 100 | } 101 | 102 | template 103 | void CircularQueue::push(const T& value) { 104 | if (count == capacity) { 105 | resize (capacity * 2); 106 | } 107 | 108 | data[backIndex++] = value; 109 | backIndex %= capacity; 110 | 111 | count++; 112 | } 113 | 114 | template 115 | void CircularQueue::pop() { 116 | if(empty()) { 117 | throw new std::runtime_error("Trying to pop out of an empty queue!\n"); 118 | } 119 | 120 | frontIndex++; 121 | frontIndex %= capacity; 122 | 123 | count--; 124 | } 125 | 126 | template 127 | const T& CircularQueue::front() const { 128 | if(empty()) { 129 | throw new std::runtime_error("Trying to access front element of empty queue!\n"); 130 | } 131 | 132 | return data[frontIndex]; 133 | } 134 | 135 | template 136 | bool CircularQueue::empty() const { 137 | return count == 0; 138 | } 139 | 140 | template 141 | int CircularQueue::size() const { 142 | return count; 143 | } 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /sem04/linked-queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | Node* next; 9 | 10 | Node(T data) : data(data), next(nullptr) {} 11 | }; 12 | 13 | template 14 | class LinkedQueue 15 | { 16 | private: 17 | Node* head; 18 | Node* tail; 19 | 20 | void copy(const LinkedQueue& other); 21 | void free(); 22 | void nullify(); 23 | 24 | public: 25 | LinkedQueue(); 26 | LinkedQueue(const LinkedQueue& other); 27 | LinkedQueue(LinkedQueue&& other) noexcept; 28 | LinkedQueue& operator=(const LinkedQueue& other); 29 | LinkedQueue& operator=(LinkedQueue&& other) noexcept; 30 | ~LinkedQueue(); 31 | 32 | void enqueue(const T& value); 33 | void dequeue(); 34 | 35 | T front() const; 36 | 37 | bool empty() const; 38 | }; 39 | 40 | template 41 | void LinkedQueue::copy(const LinkedQueue& other) 42 | { 43 | Node* iter = other.head; 44 | 45 | while(iter != nullptr) 46 | { 47 | enqueue(iter->data); 48 | iter = iter->next; 49 | } 50 | } 51 | 52 | template 53 | void LinkedQueue::free() 54 | { 55 | Node* iter = head; 56 | while (iter != nullptr) 57 | { 58 | Node* toDelete = iter; 59 | iter = iter->next; 60 | delete toDelete; 61 | } 62 | } 63 | 64 | template 65 | void LinkedQueue::nullify() 66 | { 67 | head = nullptr; 68 | tail = nullptr; 69 | } 70 | 71 | template 72 | LinkedQueue::LinkedQueue() 73 | : head(nullptr), tail(nullptr) {} 74 | 75 | template 76 | LinkedQueue::LinkedQueue(const LinkedQueue& other) : LinkedQueue() 77 | { 78 | copy(other); 79 | } 80 | 81 | template 82 | LinkedQueue::LinkedQueue(LinkedQueue&& other) noexcept 83 | : head(std::move(other.head)), tail(std::move(other.tail)) 84 | { 85 | other.nullify(); 86 | } 87 | 88 | template 89 | LinkedQueue& LinkedQueue::operator=(const LinkedQueue& other) 90 | { 91 | if (this != &other) 92 | { 93 | free(); 94 | copy(other); 95 | } 96 | return *this; 97 | } 98 | 99 | template 100 | LinkedQueue& LinkedQueue::operator=(LinkedQueue&& other) noexcept 101 | { 102 | free(); 103 | 104 | head = std::move(other.head); 105 | tail = std::move(other.tail); 106 | 107 | other.nullify(); 108 | 109 | return *this; 110 | } 111 | 112 | template 113 | LinkedQueue::~LinkedQueue() 114 | { 115 | free(); 116 | } 117 | 118 | template 119 | void LinkedQueue::enqueue(const T& value) 120 | { 121 | Node* newNode = new Node(value); 122 | 123 | if (empty()) 124 | { 125 | head = newNode; 126 | tail = newNode; 127 | } 128 | else 129 | { 130 | tail->next = newNode; 131 | tail = newNode; 132 | } 133 | } 134 | 135 | template 136 | void LinkedQueue::dequeue() 137 | { 138 | if (empty()) 139 | { 140 | throw std::runtime_error("Pop called on an empty queue."); 141 | } 142 | 143 | if (head == tail) 144 | { 145 | delete head; 146 | head = nullptr; 147 | tail = nullptr; 148 | } 149 | else 150 | { 151 | Node* toDelete = head; 152 | head = head->next; 153 | delete toDelete; 154 | } 155 | } 156 | 157 | template 158 | T LinkedQueue::front() const 159 | { 160 | if (empty()) 161 | { 162 | throw std::runtime_error("Front called on an empty queue."); 163 | } 164 | 165 | return head->data; 166 | } 167 | 168 | template 169 | bool LinkedQueue::empty() const 170 | { 171 | return head == nullptr; 172 | } 173 | 174 | int main() 175 | { 176 | LinkedQueue q; 177 | q.enqueue(1); 178 | q.enqueue(2); 179 | q.enqueue(3); 180 | q.enqueue(4); 181 | q.enqueue(5); 182 | 183 | std::cout << q.front() << std::endl; q.dequeue(); 184 | std::cout << q.front() << std::endl; q.dequeue(); 185 | std::cout << q.front() << std::endl; q.dequeue(); 186 | std::cout << q.front() << std::endl; q.dequeue(); 187 | std::cout << q.front() << std::endl; q.dequeue(); 188 | 189 | std::cout << q.empty() << std::endl; 190 | 191 | q.enqueue(1); 192 | q.enqueue(2); 193 | q.enqueue(3); 194 | q.enqueue(4); 195 | q.enqueue(5); 196 | 197 | LinkedQueue q2 = q; 198 | std::cout << q2.front() << std::endl; 199 | q2.dequeue(); 200 | std::cout << q2.front() << std::endl; 201 | q2.dequeue(); 202 | std::cout << q2.front() << std::endl; 203 | q2.dequeue(); 204 | std::cout << q2.front() << std::endl; 205 | q2.dequeue(); 206 | std::cout << q2.front() << std::endl; 207 | q2.dequeue(); 208 | } -------------------------------------------------------------------------------- /sem04/n-binary-numbers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void generate(int n) 6 | { 7 | std::queue q; 8 | q.push("1"); 9 | 10 | for (int i = 0; i < n; i++) 11 | { 12 | q.push(q.front() + "0"); 13 | q.push(q.front() + "1"); 14 | 15 | std::cout << q.front() << ' '; 16 | q.pop(); 17 | } 18 | std::cout << '\n'; 19 | } 20 | 21 | int main() 22 | { 23 | int n = 10; 24 | generate(n); 25 | 26 | n = 16; 27 | generate(n); 28 | } -------------------------------------------------------------------------------- /sem04/shortest-path-in-maze.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ROWS 9 5 | #define COLS 10 6 | 7 | struct Position 8 | { 9 | int x; 10 | int y; 11 | 12 | Position(int x, int y) 13 | : x(x), y(y) {} 14 | 15 | bool operator==(const Position& rhs) 16 | { 17 | return x == rhs.x && y== rhs.y; 18 | } 19 | }; 20 | 21 | struct Cell 22 | { 23 | Position pos; 24 | int steps; 25 | 26 | Cell(Position pos, int steps) 27 | : pos(pos), steps(steps) {} 28 | }; 29 | 30 | bool isValid(int row, int col) 31 | { 32 | return row >= 0 && row < ROWS && 33 | col >= 0 && col < COLS; 34 | } 35 | 36 | int getShortestPath(bool maze[][COLS], Position start, Position end) 37 | { 38 | // Не можем да се движим по диагонал, затова валидните движения са: 39 | int movesCount = 4; 40 | std::vector moves{{0, -1}, {0, 1}, {1, 0}, {-1, 0}}; 41 | 42 | // Матрица за посетените полета 43 | bool visited[ROWS][COLS]; 44 | for (int rows = 0; rows < ROWS; rows++) 45 | { 46 | for (int i = 0; i < COLS; i++) 47 | { 48 | visited[rows][i] = false; 49 | } 50 | } 51 | 52 | // Първото посетено поле е това, от което започваме 53 | visited[start.x][start.y] = true; 54 | 55 | // Опашка за достигнатите полета 56 | std::queue q; 57 | 58 | // Първото достигнато поле е началното. То е достигнато с 0 стъпки 59 | q.push(Cell(start, 0)); 60 | 61 | while (!q.empty()) 62 | { 63 | Cell current = q.front(); 64 | q.pop(); 65 | 66 | // Проверка дали сме достигнали финалното поле 67 | if (current.pos == end) 68 | { 69 | return current.steps; 70 | } 71 | 72 | // Генериране на достижимите полета от текущото 73 | for (int i = 0; i < movesCount; i++) 74 | { 75 | // Координатите на достижимо поле получаваме като направим изместването moves[i] 76 | int newX = current.pos.x + moves[i].x; 77 | int newY = current.pos.y + moves[i].y; 78 | 79 | // 1. Ако тези координати са валидни координати за лабиринта и 80 | // 2. полето, което отговаря на тях все още не е било посещавано и 81 | // 3. през него може да се мине (т.е. представено е с 1 в лабиринта, а е 0 е стена), 82 | // то го добавяме в опашката заедно с броя стъпки, с които е достигнато и 83 | // го отбелязваме като посетено в матрицата. 84 | if (isValid(newX, newY) && !visited[newX][newY] && maze[newX][newY]) 85 | { 86 | visited[newX][newY] = true; 87 | q.push(Cell(Position(newX, newY), current.steps + 1)); 88 | } 89 | } 90 | } 91 | 92 | return -1; 93 | } 94 | 95 | 96 | int main() 97 | { 98 | bool maze[ROWS][COLS] = 99 | { 100 | { 1, 0, 1, 1, 1, 1, 0, 1, 1, 1 }, 101 | { 1, 0, 1, 0, 1, 1, 1, 0, 1, 1 }, 102 | { 1, 1, 1, 0, 1, 1, 0, 1, 0, 1 }, 103 | { 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, 104 | { 1, 1, 1, 0, 1, 1, 1, 0, 1, 0 }, 105 | { 1, 0, 1, 1, 1, 1, 0, 1, 0, 0 }, 106 | { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 }, 107 | { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 }, 108 | { 1, 1, 0, 0, 0, 0, 1, 0, 0, 1 } 109 | }; 110 | 111 | Position start = {0, 0}; 112 | 113 | Position end0 = {7, 1}; 114 | std::cout << getShortestPath(maze, start, end0) << std::endl; 115 | 116 | Position end1 = {0, 0}; 117 | std::cout << getShortestPath(maze, start, end1) << std::endl; 118 | 119 | Position end2 = {3, 4}; 120 | std::cout << getShortestPath(maze, start, end2) << std::endl; 121 | 122 | Position end3 = {8, 9}; 123 | std::cout << getShortestPath(maze, start, end3) << std::endl; 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /sem05/01-detect-cycle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T value; 7 | Node* next; 8 | 9 | Node(const T& value, Node* next = nullptr) 10 | : value(value), next(next) {} 11 | }; 12 | 13 | template 14 | bool containsCycle(Node* head) 15 | { 16 | Node* slow = head; 17 | Node* fast = head; 18 | 19 | bool hasCycle = false; 20 | while (slow && fast && fast->next && !hasCycle) 21 | { 22 | slow = slow->next; 23 | fast = fast->next->next; 24 | 25 | if (slow == fast) 26 | { 27 | hasCycle = true; 28 | } 29 | } 30 | 31 | return hasCycle; 32 | } 33 | 34 | int main() 35 | { 36 | Node* n10 = new Node(12, nullptr); 37 | Node* n9 = new Node(10, n10); 38 | Node* n8 = new Node(11, n9); 39 | Node* n7 = new Node(1, n8); 40 | Node* n6 = new Node(82, n7); 41 | Node* n5 = new Node(64, n6); 42 | Node* n4 = new Node(10, n5); 43 | Node* n3 = new Node(17, n4); 44 | Node* n2 = new Node(2, n3); 45 | Node* n1 = new Node(8, n2); 46 | // n10->next = n7; 47 | 48 | std::cout << containsCycle(n1); 49 | } -------------------------------------------------------------------------------- /sem05/02-nth-element-backwards.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T value; 7 | Node* next; 8 | 9 | Node(const T& value, Node* next = nullptr) 10 | : value(value), next(next) {} 11 | }; 12 | 13 | // 1st solution 14 | template 15 | T getElementN(Node* head, int n) 16 | { 17 | Node* fast = head; 18 | Node* slow = head; 19 | 20 | while (n > 0 && fast && fast->next) 21 | { 22 | fast = fast->next; 23 | n--; 24 | } 25 | 26 | while (slow && fast) 27 | { 28 | slow = slow->next; 29 | fast = fast->next; 30 | } 31 | 32 | return slow->value; 33 | } 34 | 35 | // 2nd solution 36 | template 37 | int getListLength(Node* head) 38 | { 39 | Node* iter = head; 40 | int length = 0; 41 | 42 | while (iter) 43 | { 44 | iter = iter->next; 45 | length++; 46 | } 47 | 48 | return length; 49 | } 50 | 51 | template 52 | T getElementN_(Node* head, int n) 53 | { 54 | Node* iter = head; 55 | int end = getListLength(head) - n; 56 | 57 | for (int i = 0; i < end; i++) 58 | { 59 | iter = iter->next; 60 | } 61 | 62 | return iter->value; 63 | } 64 | 65 | int main() 66 | { 67 | Node* n10 = new Node(12, nullptr); 68 | Node* n9 = new Node(10, n10); 69 | Node* n8 = new Node(11, n9); 70 | Node* n7 = new Node(1, n8); 71 | Node* n6 = new Node(82, n7); 72 | Node* n5 = new Node(64, n6); 73 | Node* n4 = new Node(10, n5); 74 | Node* n3 = new Node(17, n4); 75 | Node* n2 = new Node(2, n3); 76 | Node* n1 = new Node(8, n2); 77 | 78 | std::cout << getElementN(n1, 3) << '\n'; 79 | std::cout << getElementN_(n1, 7) << '\n'; 80 | } -------------------------------------------------------------------------------- /sem05/03-remove-duplicates.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T value; 7 | Node* prev; 8 | Node* next; 9 | 10 | Node(const T& value, Node* prev = nullptr, Node* next = nullptr) 11 | : value(value), prev(prev), next(next) {} 12 | }; 13 | 14 | template 15 | void removeDuplicates(Node* head) 16 | { 17 | Node* iter = head; 18 | 19 | while (iter && iter->next) 20 | { 21 | if (iter->value == iter->next->value) 22 | { 23 | if (iter->next->next) 24 | { 25 | Node* toDelete = iter->next; 26 | iter->next = iter->next->next; 27 | iter->next->prev = iter; 28 | delete toDelete; 29 | } 30 | else 31 | { 32 | Node* toDelete = iter->next; 33 | iter->next = nullptr; 34 | delete toDelete; 35 | } 36 | } 37 | else 38 | { 39 | iter = iter->next; 40 | } 41 | } 42 | } 43 | 44 | template 45 | void print(Node* head) 46 | { 47 | Node* iter = head; 48 | 49 | std::cout << "[ "; 50 | while (iter->next) 51 | { 52 | std::cout << iter->value << " <-> "; 53 | iter = iter->next; 54 | } 55 | std::cout << iter->value << " ]"; 56 | } 57 | 58 | int main() 59 | { 60 | Node* n1 = new Node(8); 61 | Node* n2 = new Node(8, n1); 62 | Node* n3 = new Node(17, n2); 63 | Node* n4 = new Node(17, n3); 64 | Node* n5 = new Node(64, n4); 65 | Node* n6 = new Node(64, n5); 66 | Node* n7 = new Node(64, n6); 67 | Node* n8 = new Node(2, n7); 68 | Node* n9 = new Node(10, n8); 69 | Node* n10 = new Node(12, n9); 70 | 71 | n1->next = n2; 72 | n2->next = n3; 73 | n3->next = n4; 74 | n4->next = n5; 75 | n5->next = n6; 76 | n6->next = n7; 77 | n7->next = n8; 78 | n8->next = n9; 79 | n9->next = n10; 80 | 81 | removeDuplicates(n1); 82 | print(n1); 83 | } 84 | -------------------------------------------------------------------------------- /sem05/05-merge-sort-doubly-linked-list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T value; 7 | Node* prev; 8 | Node* next; 9 | 10 | Node(const T& value, Node* prev = nullptr, Node* next = nullptr) 11 | : value(value), prev(prev), next(next) {} 12 | }; 13 | 14 | template 15 | Node* merge(Node* first, Node* second) 16 | { 17 | if (first == nullptr) 18 | { 19 | return second; 20 | } 21 | 22 | if (second == nullptr) 23 | { 24 | return first; 25 | } 26 | 27 | if (first->value <= second->value) 28 | { 29 | first->next = merge(first->next, second); 30 | first->next->prev = first; 31 | first->prev = nullptr; 32 | return first; 33 | } 34 | else 35 | { 36 | second->next = merge(first, second->next); 37 | second->next->prev = second; 38 | second->prev = nullptr; 39 | return second; 40 | } 41 | 42 | } 43 | 44 | template 45 | Node* getSecondHalf(Node* head) 46 | { 47 | Node* slow = head; 48 | Node* fast = head; 49 | 50 | while (fast->next && fast->next->next) 51 | { 52 | slow = slow->next; 53 | fast = fast->next->next; 54 | } 55 | 56 | Node* secondHalf = slow->next; 57 | slow->next = nullptr; 58 | return secondHalf; 59 | } 60 | 61 | template 62 | Node* mergeSort(Node* head) 63 | { 64 | if (head == nullptr || head->next == nullptr) 65 | { 66 | return head; 67 | } 68 | 69 | Node* secondHalf = getSecondHalf(head); 70 | 71 | head = mergeSort(head); 72 | secondHalf = mergeSort(secondHalf); 73 | return merge(head, secondHalf); 74 | } 75 | 76 | 77 | template 78 | void print(Node* head) 79 | { 80 | if (head == nullptr) 81 | { 82 | std::cout << "[]"; 83 | return; 84 | } 85 | 86 | Node* iter = head; 87 | 88 | std::cout << "[ "; 89 | while (iter->next) 90 | { 91 | std::cout << iter->value << " <-> "; 92 | iter = iter->next; 93 | } 94 | std::cout << iter->value << " ]"; 95 | } 96 | 97 | int main() 98 | { 99 | Node* n1 = new Node(8); 100 | Node* n2 = new Node(2, n1); 101 | Node* n3 = new Node(17, n2); 102 | Node* n4 = new Node(1, n3); 103 | Node* n5 = new Node(6, n4); 104 | Node* n6 = new Node(4, n5); 105 | Node* n7 = new Node(64, n6); 106 | Node* n8 = new Node(12, n7); 107 | Node* n9 = new Node(10, n8); 108 | Node* n10 = new Node(3, n9); 109 | 110 | n1->next = n2; 111 | n2->next = n3; 112 | n3->next = n4; 113 | n4->next = n5; 114 | n5->next = n6; 115 | n6->next = n7; 116 | n7->next = n8; 117 | n8->next = n9; 118 | n9->next = n10; 119 | 120 | Node* sorted = mergeSort(n1); 121 | print(sorted); 122 | } -------------------------------------------------------------------------------- /sem05/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 5 2 | # Списък 3 | 4 | ## I. Списък 5 | Списъкът представлява крайна редица от еднотипни (хомогенни) елементи.
6 | 7 | **Основни операции** 8 | - добавяне на елемент в началото/края/на произволна позиция 9 | - премахване на елемент от началото/края/произволна позиция 10 |

11 | 12 | ![Linked lists](../media/sem05/sem05-linked-lists.png) 13 | 14 | ## II. Свързан списък с една връзка 15 | 16 | ## III. Свързан списък с две връзки 17 | 18 | ## IV. Анализ на сложността 19 | 20 | ![Add element in linked list](../media/sem05/sem05-one-two-list.png) 21 | 22 | | Основна операция | Едносвързан списък | Двусвързан списък | 23 | | --- | --- | --- | 24 | | Добавяне на елемент в началото | *O(1)* | *O(1)* | 25 | | Добавяне на елемент в края | *O(1)* | *O(1)* | 26 | | Премахване на елемент от началото | *O(1)* | *O(1)* | 27 | | Премахване на елемент от края | *O(n)* | *O(1)* | 28 | | Достъп до първия елемент | *O(1)* | *O(1)* | 29 | | Достъп до последния елемент | *O(1)* | *O(1)* | 30 | 31 | ## V. Задачи 32 | 33 | ### **Задача 1.** 34 | Да се напише функция, която проверява дали в свързан списък съществува цикъл. 35 | 36 | ### **Задача 2.** 37 | Да се напише функция, която получава указател към главата на списък и естествено число n и връща стойността на елемента, който се намира на n позиции от опашката на списъка. 38 | 39 | Пример: 40 | 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 41 | n = 3 42 | Изход: 5 43 | 44 | ### **Задача 3.** 45 | Да се напише функция, която премахва дублиращи се елементи от сортиран свързан списък. 46 | 47 | ### **Задача 4.** 48 | Да се напише функция, която получава указател към главата на свързан списък от цели числа и мести възлите съдържащи четни стойности в края на списъка, но в обратен ред на срещането им. 49 | 50 | ### **Задача 5.** 51 | Да се напише функция, която получава указател към главата на свързан списък от цели числа и го сортира. 52 | -------------------------------------------------------------------------------- /sem05/singly-linked-list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T value; 8 | Node* next; 9 | 10 | Node(const T& value) 11 | : value(value), next(nullptr) {} 12 | }; 13 | 14 | template 15 | class SinglyLinkedList 16 | { 17 | private: 18 | Node* head; 19 | Node* tail; 20 | 21 | void copy(const SinglyLinkedList& other); 22 | void free(); 23 | 24 | void swap(SinglyLinkedList& other); 25 | 26 | public: 27 | SinglyLinkedList(); 28 | SinglyLinkedList(const SinglyLinkedList& other); 29 | SinglyLinkedList(SinglyLinkedList&& other); 30 | SinglyLinkedList& operator=(const SinglyLinkedList& other); 31 | SinglyLinkedList& operator=(SinglyLinkedList&& other); 32 | ~SinglyLinkedList(); 33 | 34 | void clear(); // clears the contents 35 | 36 | void pushFront(const T& value); // O(1) 37 | void pushBack(const T& value); // O(1) 38 | 39 | void popFront(); // O(1) 40 | void popBack(); // O(n) 41 | 42 | const T& front(); // O(1) 43 | const T& back(); // O(1) 44 | 45 | bool empty(); 46 | 47 | template 48 | friend void swap(SinglyLinkedList& lhs, SinglyLinkedList rhs); 49 | 50 | template 51 | friend std::ostream& operator<<(std::ostream& os, const SinglyLinkedList& other); 52 | }; 53 | 54 | template 55 | void SinglyLinkedList::copy(const SinglyLinkedList& other) 56 | { 57 | Node* iter = other.head; 58 | 59 | while (iter != nullptr) 60 | { 61 | pushBack(iter->value); 62 | iter = iter->next; 63 | } 64 | } 65 | 66 | template 67 | void SinglyLinkedList::free() 68 | { 69 | while(!empty()) 70 | { 71 | popFront(); 72 | } 73 | 74 | head = nullptr; 75 | tail = nullptr; 76 | } 77 | 78 | template 79 | void SinglyLinkedList::swap(SinglyLinkedList& other) 80 | { 81 | std::swap(head, other.head); 82 | std::swap(tail, other.tail); 83 | } 84 | 85 | template 86 | SinglyLinkedList::SinglyLinkedList() 87 | : head(nullptr), tail(nullptr) {} 88 | 89 | template 90 | SinglyLinkedList::SinglyLinkedList(const SinglyLinkedList& other) 91 | : head(nullptr), tail(nullptr) 92 | { 93 | copy(other); 94 | } 95 | 96 | template 97 | SinglyLinkedList::SinglyLinkedList(SinglyLinkedList&& other) 98 | : head(std::move(other.head)), tail(std::move(other.tail)) 99 | { 100 | other.head = nullptr; 101 | other.tail = nullptr; 102 | } 103 | 104 | // assignment operator has a strong runtime_error guarantee 105 | // using the copy-and-swap idiom 106 | template 107 | SinglyLinkedList& SinglyLinkedList::operator=(const SinglyLinkedList& other) 108 | { 109 | SinglyLinkedList temp(other); 110 | swap(temp); 111 | return *this; 112 | } 113 | 114 | template 115 | SinglyLinkedList& SinglyLinkedList::operator=(SinglyLinkedList&& other) 116 | { 117 | SinglyLinkedList temp(std::move(other)); 118 | swap(temp); 119 | return *this; 120 | } 121 | 122 | template 123 | SinglyLinkedList::~SinglyLinkedList() 124 | { 125 | free(); 126 | } 127 | 128 | template 129 | void SinglyLinkedList::clear() 130 | { 131 | free(); 132 | } 133 | 134 | template 135 | void SinglyLinkedList::pushFront(const T& value) 136 | { 137 | Node* newNode = new Node(value); 138 | 139 | if (empty()) 140 | { 141 | head = newNode; 142 | tail = newNode; 143 | } 144 | else 145 | { 146 | newNode->next = head; 147 | head = newNode; 148 | } 149 | } 150 | 151 | template 152 | void SinglyLinkedList::pushBack(const T& value) 153 | { 154 | Node* newNode = new Node(value); 155 | 156 | if (empty()) 157 | { 158 | head = newNode; 159 | tail = newNode; 160 | } 161 | else 162 | { 163 | tail->next = newNode; 164 | tail = newNode; 165 | } 166 | } 167 | 168 | template 169 | void SinglyLinkedList::popFront() 170 | { 171 | if (empty()) 172 | { 173 | throw std::runtime_error("Empty list."); 174 | } 175 | 176 | if (head == tail) 177 | { 178 | delete head; 179 | head = nullptr; 180 | tail = nullptr; 181 | } 182 | else 183 | { 184 | Node* toDelete = head; 185 | head = head->next; 186 | delete toDelete; 187 | } 188 | } 189 | 190 | template 191 | void SinglyLinkedList::popBack() 192 | { 193 | if (empty()) 194 | { 195 | throw std::runtime_error("Empty list."); 196 | } 197 | 198 | if (head == tail) 199 | { 200 | delete head; 201 | head = nullptr; 202 | tail = nullptr; 203 | } 204 | else 205 | { 206 | Node* newTail = head; 207 | 208 | while (newTail->next != tail) 209 | { 210 | newTail = newTail->next; 211 | } 212 | 213 | delete tail; 214 | newTail->next = nullptr; 215 | tail = newTail; 216 | } 217 | 218 | } 219 | 220 | template 221 | const T& SinglyLinkedList::front() 222 | { 223 | if (empty()) 224 | { 225 | throw std::runtime_error("Empty list."); 226 | } 227 | 228 | return head->value; 229 | } 230 | 231 | template 232 | const T& SinglyLinkedList::back() 233 | { 234 | if (empty()) 235 | { 236 | throw std::runtime_error("Empty list."); 237 | } 238 | 239 | return tail->value; 240 | } 241 | 242 | template 243 | bool SinglyLinkedList::empty() 244 | { 245 | return head == nullptr; 246 | } 247 | 248 | template 249 | void swap(SinglyLinkedList& lhs, SinglyLinkedList rhs) 250 | { 251 | lhs.swap(rhs); 252 | } 253 | 254 | template 255 | std::ostream& operator<<(std::ostream& os, const SinglyLinkedList& other) 256 | { 257 | Node* iter = other.head; 258 | 259 | if (iter == nullptr) 260 | { 261 | os << "[]"; 262 | return os; 263 | } 264 | 265 | if (iter == other.tail) 266 | { 267 | os << "[ " << iter->value << " ]"; 268 | return os; 269 | } 270 | 271 | os << "[ "; 272 | while (iter != other.tail) 273 | { 274 | os << iter->value << " -> "; 275 | iter = iter->next; 276 | } 277 | os << iter->value << " ]"; 278 | 279 | return os; 280 | } 281 | 282 | int main() 283 | { 284 | SinglyLinkedList s; 285 | s.pushFront(1); 286 | s.pushBack(2); 287 | s.popFront(); 288 | s.pushBack(12); 289 | s.pushFront(10); 290 | s.popBack(); 291 | 292 | SinglyLinkedList s1 = s; 293 | SinglyLinkedList s2; 294 | s2.pushFront(14); 295 | s2.pushFront(18); 296 | s2.pushBack(10); 297 | 298 | std::cout << s2 << '\n'; 299 | s2 = s1; 300 | 301 | s1.pushFront(12); 302 | s2.clear(); 303 | 304 | std::cout << s << '\n' << s1 << '\n' << s2 << '\n'; 305 | } -------------------------------------------------------------------------------- /sem06/01-merge.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::forward_list merge(std::forward_list& l1, std::forward_list& l2) 5 | { 6 | std::forward_list::iterator it1 = l1.begin(); 7 | std::forward_list::iterator it2 = l2.begin(); 8 | 9 | std::forward_list l; 10 | std::forward_list::iterator it = l.before_begin(); 11 | 12 | while (it1 != l1.end() && it2 != l2.end()) 13 | { 14 | if ((*it1) <= (*it2)) 15 | { 16 | l.insert_after(it, *it1); 17 | it++; 18 | it1++; 19 | } 20 | else 21 | { 22 | l.insert_after(it, *it2); 23 | it++; 24 | it2++; 25 | } 26 | } 27 | 28 | while (it1 != l1.end()) 29 | { 30 | l.insert_after(it, *it1); 31 | it++; 32 | it1++; 33 | } 34 | 35 | while (it2 != l2.end()) 36 | { 37 | l.insert_after(it, *it2); 38 | it++; 39 | it2++; 40 | } 41 | 42 | return l; 43 | } 44 | 45 | int main() 46 | { 47 | std::forward_list l1; 48 | l1.push_front(20); 49 | l1.push_front(16); 50 | l1.push_front(8); 51 | l1.push_front(4); 52 | l1.push_front(1); 53 | std::forward_list l2; 54 | l2.push_front(15); 55 | l2.push_front(5); 56 | l2.push_front(3); 57 | l2.push_front(1); 58 | l2.push_front(0); 59 | 60 | std::forward_list l = merge(l1, l2); 61 | 62 | for (std::forward_list::iterator it = l.begin(); it != l.end(); it++) 63 | { 64 | std::cout << *it << ' '; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /sem06/02-insert.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void insert(std::list& l, int x) 5 | { 6 | std::list::iterator it = l.begin(); 7 | 8 | bool inserted = false; 9 | 10 | while (it != l.end()) 11 | { 12 | if ((*it) >= x) 13 | { 14 | l.insert(it, x); 15 | inserted = true; 16 | break; 17 | } 18 | 19 | it++; 20 | } 21 | 22 | if (!inserted) 23 | { 24 | l.insert(it, x); 25 | } 26 | } 27 | 28 | void printList(const std::list& list) 29 | { 30 | for (int el : list) 31 | { 32 | std::cout << el << " -> "; 33 | } 34 | std::cout << "end\n"; 35 | } 36 | 37 | int main() 38 | { 39 | std::list l; 40 | l.push_front(6); 41 | l.push_front(5); 42 | l.push_front(3); 43 | l.push_front(2); 44 | l.push_front(1); 45 | int x = 0; 46 | insert(l, x); 47 | x = 7; 48 | insert(l, x); 49 | x = 4; 50 | insert(l, x); 51 | printList(l); 52 | } -------------------------------------------------------------------------------- /sem06/03-rearrange.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Node 4 | { 5 | int value; 6 | Node* next; 7 | 8 | Node(const int& value, Node* next = nullptr) 9 | : value(value), next(next) {} 10 | }; 11 | 12 | Node* rearrange(Node* head) 13 | { 14 | Node* evenHead = nullptr; 15 | Node* evenTail = nullptr; 16 | 17 | Node* oddHead = nullptr; 18 | Node* oddTail = nullptr; 19 | 20 | Node* iter = head; 21 | while (iter) 22 | { 23 | if (iter->value % 2 == 0) 24 | { 25 | if (evenHead == nullptr) 26 | { 27 | evenHead = iter; 28 | evenTail = iter; 29 | } 30 | else 31 | { 32 | evenTail->next = iter; 33 | evenTail = iter; 34 | } 35 | } 36 | else 37 | { 38 | if (oddHead == nullptr) 39 | { 40 | oddHead = iter; 41 | oddTail = iter; 42 | } 43 | else 44 | { 45 | oddTail->next = iter; 46 | oddTail = iter; 47 | } 48 | } 49 | 50 | iter = iter->next; 51 | } 52 | 53 | if (evenTail) 54 | { 55 | evenTail->next = oddHead; 56 | } 57 | 58 | if (oddTail) 59 | { 60 | oddTail->next = nullptr; 61 | } 62 | 63 | return evenHead ? evenHead : oddHead; 64 | } 65 | 66 | void print(Node* head) 67 | { 68 | if (head == nullptr) 69 | { 70 | std::cout << "[]"; 71 | return; 72 | } 73 | 74 | Node* iter = head; 75 | while (iter->next) 76 | { 77 | std::cout << iter->value << " -> "; 78 | iter = iter->next; 79 | } 80 | std::cout << iter->value << " ]"; 81 | } 82 | 83 | int main() 84 | { 85 | Node* n1 = new Node(3); 86 | Node* n2 = new Node(8); 87 | Node* n3 = new Node(1); 88 | Node* n4 = new Node(7); 89 | Node* n5 = new Node(4); 90 | Node* n6 = new Node(2); 91 | Node* n7 = new Node(9); 92 | Node* n8 = new Node(6); 93 | Node* n9 = new Node(5); 94 | Node* n10 = new Node(0); 95 | 96 | n1->next = n2; 97 | n2->next = n3; 98 | n3->next = n4; 99 | n4->next = n5; 100 | n5->next = n6; 101 | n6->next = n7; 102 | n7->next = n8; 103 | n8->next = n9; 104 | n9->next = n10; 105 | 106 | Node* newNode = rearrange(n1); 107 | print(newNode); 108 | } 109 | -------------------------------------------------------------------------------- /sem06/04-reverse-in-groups.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Node 5 | { 6 | int value; 7 | Node* next; 8 | 9 | Node(int value, Node* next) 10 | : value(value), next(next) {} 11 | }; 12 | 13 | // Решение, предложено от вас 14 | Node* ReverseEveryK(Node* head, int k) 15 | { 16 | std::stack stack; 17 | 18 | Node* newhead = nullptr; 19 | Node* iter = nullptr; 20 | 21 | while(head) 22 | { 23 | int counter = 0; 24 | 25 | while(head && counter < k) 26 | { 27 | Node* afterHead = head->next; 28 | head->next = nullptr; 29 | stack.push(head); 30 | head = afterHead; 31 | ++counter; 32 | } 33 | 34 | if (!newhead) 35 | { 36 | newhead = stack.top(); 37 | stack.pop(); 38 | iter = newhead; 39 | } 40 | 41 | while(!stack.empty()) 42 | { 43 | iter->next = stack.top(); 44 | stack.pop(); 45 | iter = iter->next; 46 | } 47 | 48 | } 49 | 50 | return newhead; 51 | } 52 | 53 | Node* reverseK(Node** current, int k) 54 | { 55 | Node* prev = nullptr; 56 | int count = k; 57 | 58 | while (*current && count > 0) 59 | { 60 | Node* next = (*current)->next; 61 | (*current)->next = prev; 62 | 63 | prev = *current; 64 | *current = next; 65 | 66 | count--; 67 | } 68 | 69 | return prev; 70 | } 71 | 72 | Node* reverse(Node* head, int k) 73 | { 74 | if (head == nullptr) 75 | { 76 | return nullptr; 77 | } 78 | 79 | Node* current = head; 80 | 81 | Node* prev = reverseK(¤t, k); 82 | head->next = reverse(current, k); 83 | return prev; 84 | } 85 | 86 | void push(Node** head, int data) 87 | { 88 | Node* newNode = new Node(data, *head); 89 | *head = newNode; 90 | } 91 | 92 | void print(Node* head) 93 | { 94 | if (head == nullptr) 95 | { 96 | std::cout << "[]"; 97 | return; 98 | } 99 | 100 | Node* iter = head; 101 | std::cout << "[ "; 102 | while (iter->next) 103 | { 104 | std::cout << iter->value << " -> "; 105 | iter = iter->next; 106 | } 107 | std::cout << iter->value << " ]"; 108 | } 109 | 110 | int main() 111 | { 112 | int keys[] = {8, 7, 6, 5, 4, 3, 2, 1}; 113 | int size = sizeof(keys) / sizeof(keys[0]); 114 | 115 | Node* head = nullptr; 116 | for (int i = 0; i < size; i++) 117 | { 118 | push(&head, keys[i]); 119 | } 120 | 121 | print(head); 122 | head = reverse(head, 3); 123 | print(head); 124 | head = ReverseEveryK(head, 3); 125 | print(head); 126 | 127 | } 128 | -------------------------------------------------------------------------------- /sem06/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 6 2 | # Задачи върху линейни структури от данни 3 | 4 | ### **Задача 1.** 5 | Да се напише функция, която прави merge на два сортирани списъка с една връзка. 6 | 7 | ### **Задача 2.** 8 | Даден е свързан списък с две връзки, чиито елементи са във възходящ ред. Да се напише функция, която добавя елемент X на такова място в списъка, че той да остане сортиран. 9 | 10 | ### **Задача 3.** 11 | Даден е списък с една връзка, чийто елементи са цели числа. Да се напише функция, която подрежда елементите на списъка така че първо да са всички четни числа, а след това всички нечетни. Относителната наредба между числата трябва да се запази. Решението ви трябва да работи за O(n) по време. 12 | 13 | Вход: 7->5->8->9->2->6->4->11 14 | Изход: 8->2->6->4->7->5->9->11 15 | 16 | ### **Задача 4.** 17 | Да се напише функция, която обръща всяка група от k елемента в свързан списък. 18 | 19 | Вход: 1 -> 2 -> 4 -> 5 -> 6 , k = 2 20 | Изход: 2 -> 1 -> 5 -> 4 -> 6 21 | 22 | Вход: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 , k = 3 23 | Изход: 3 -> 2 -> 1 -> 6 -> 5 -> 4 -> 8 -> 7 24 | 25 | ### **Задача 5.** 26 | Дадени са 2 стека с елементи естествени числа и естествено число N. Да се напише функция, която намира максималният брой елементи, които могат да бъдат премахнати от двата стека, чиято обща сума не надминава N. 27 | 28 | ### :pencil2: **Задача 6.** 29 | Реализирайте функцията append, която по подадени два списъка ги обединявя. 30 | 31 | Пример: 32 | append({1,5,6,3},{1,6}) -> {1,5,6,3,1,6} 33 | 34 | ### :pencil2: **Задача 7.** 35 | Да се сортира: 36 | а) стек 37 | б) опашка 38 | в) свързан списък с една връзка 39 | -------------------------------------------------------------------------------- /sem07/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 7 2 | # Дървета. 3 | 4 | ## Двоично дърво 5 | Представя се рекурсивно.
6 | 1. Празното дърво е дърво. 7 | 2. Непразно дърво има ляво и дясно поддърво. 8 |

9 | 10 | ![Binary tree](../media/sem07/sem07-binary-tree.png) 11 | 12 |
13 | 14 | **Термини:**
15 | Листа - върховете с празни ляво и дясно поддърво.
16 | Вътрешни върхове - върховете, които не са корен или листо.
17 | Родител - наследник
18 | Ниво - коренът е на ниво 0 или 1; наследникът на връх на ниво i е на ниво i + 1
19 | Височина (дълбочина) - разстоянието от корена до листата (максималното ниво).
20 |
21 |
22 | 23 | ![Tree terminology](../media/sem07/sem07-binary-tree-terminology.png) 24 |
25 |
26 | 27 | ### Операции 28 | 29 | - пряк достъп до корена 30 | - непряк достъп до останалите върхове 31 | - добавяне и премахване на произволен връх (резултатът остава двоично дърво) 32 | - обхождане 33 | 34 | ### Физическо представяне 35 | 36 | - свързано 37 | - последователно 38 | - чрез 3 масива: за стойността върховете, за левите наследници и за десните наследници 39 | - списък на бащите 40 | 41 | ## Задачи 42 | 43 | ### **Задача 1.** 44 | Да се напише функция, която намира сумата на елементите на дадено дърво от цели числа. 45 | 46 | ### **Задача 2.** 47 | Да се напише функция, която проверява дали дадено дърво съдържа елемент X. 48 | 49 | ### **Задача 3.** 50 | Да се напише функция, която приема двоично дърво и връща броя на елементите му. 51 | 52 | ### **Задача 4.** 53 | Да се напише функция, която приема двоично дърво и връща най-големият елемент в него. В дървото има поне един елемент. 54 | 55 | ### **Задача 5.** 56 | Да се напише функция, която намира броя на листата в двоично дърво. 57 | 58 | ### **Задача 6.** 59 | Да се напише функция, която по подадено двоично дърво и елемент X връща поддървото с корен X, ако такова съществува. 60 | 61 | ### **Задача 7.** 62 | Да се напише функция, която обхожда дърво ляво-корен-дясно (inorder traversal). 63 | 64 | ### **Задача 8.** 65 | Да се напише функция, която обхожда дърво корен-ляво-дясно (preorder traversal). 66 | 67 | ### **Задача 9.** 68 | Да се напише функция, която приема указател към корена на дърво и цяло число N и принтира сумата на елементите, които се намират на ниво N. 69 | 70 | ### **Задача 10.** 71 | Напишете функция, която приема указател към корена на дърво и цяло число n и принтира елементите, които се намират на ниво n. 72 | 73 | ### **Задача 11.** 74 | Да се напише функция, която намира височината на двоично дърво.
75 | а) итеративно
76 | б) рекурсивно
77 | 78 | -------------------------------------------------------------------------------- /sem08/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 8 2 | # Двоично наредено дърво. 3 | 4 | ## Въведение 5 | Имаме проблем, в който искаме да модифицираме съхранени данни. Коя структура от данни да използваме?
6 | 7 | | операция | масив | 8 | | --- | --- | 9 | | търсене | ***O(n)*** | 10 | | добавяне | ***O(n)*** | 11 | | триене | ***O(n)*** | 12 | 13 | | операция | свързан списък | 14 | | --- | --- | 15 | | търсене | ***O(n)*** | 16 | | добавяне | ***O(n)*** | 17 | | триене | ***O(n)*** | 18 | 19 | ** Aко нямаме референция към мястото, на което искаме да извършим добавянето / триенето
20 | ** При наличие на референция операциите за добавяне и триене са с константна сложност 21 | 22 | | операция | сортиран масив | 23 | | --- | --- | 24 | | търсене | ***O(logn)*** | 25 | | добавяне | ***O(n)*** | 26 | | триене | ***O(n)*** | 27 | 28 | ** Добавянето може да бъде осъществено чрез логаритмично намиране на позицията. Самото добавяне ни струва линейно време заради shift-ването на елементите
29 | ** Аналогично за триенето 30 | 31 | ## Двоично наредено дърво 32 | Оптимизиран вариант на двоично дърво, който ни дава по-добро търсене (от там идва и името Binary Search Tree - BST).
33 | 34 | Двоично наредено дърво - двоично дърво, в което за всеки връх: 35 | - Стойностите на всички върхове в лявото му поддърво са по-малки от стойността на върха 36 | - Стойностите във всички върхове на дясното му поддърво са по-големи от стойността на върха 37 | 38 | ![Binary search tree](../media/sem08/sem08-bst.png) 39 | 40 |
41 | ⚠️ За да бъде двоичното дърво наредено, то лявото и дясното му поддърво също трябва да бъдат двоични наредени дървета. 42 |

43 | 44 | **Примери:** 45 | 46 | Валидно дърво 47 | ![Valid binary search tree](../media/sem08/sem08-bst-example.png) 48 |
49 |
50 | 51 | Невалидно дърво 52 | ![Invalid binary search tree](../media/sem08/sem08-invalid-bst-exmple.png) 53 | 54 | ## Основни операции в BST 55 | 56 | ### **1. Търсене** 57 | 58 | От къде идва сложността?
59 | Binary search: n -> n/2 -> n/4 -> ... -> 1
60 | Това са log(n) стъпки. 61 | 62 | Тази идея в двоично дърво: 63 | 64 | ![Binary search tree search](../media/sem08/sem08-bst-search.png) 65 | 66 | Кой е най-лошият случай за търсене в двоично наредено дърво? 67 |
68 |
69 | 70 | ![Degenerate BST](../media/sem08/sem08-degenerate-bst.png) 71 | 72 | ⚠️ Ако дървото е балансирано (разликата между височините на лявото и дясното поддърво е максимум 1), то n -> n/2 -> n/4 -> ... -> 1 73 | 74 | ### **2. Добавяне** 75 | 76 | ![BST insertion](../media/sem08/sem08-bst-insert.png) 77 | 78 | ### **3. Триене** 79 | 80 | ![BST deletion](../media/sem08/sem08-bst-delete-leaf-1-child.png) 81 | 82 | ![BST deletion](../media/sem08/sem08-bst-delete-2-children.png) 83 | 84 | ## Сложност на операциите 85 | 86 | | операция | двоично наредено дърво (WC) | двоично наредено дърво (AC) | 87 | | --- | --- | --- | 88 | | търсене | ***O(n)*** | ***Θ(logn)*** | 89 | | добавяне | ***O(n)*** | ***Θ(logn)*** | 90 | | триене | ***O(n)*** | ***Θ(logn)*** | 91 | 92 | | операция | балансирано дърво | 93 | | --- | --- | 94 | | търсене | ***O(logn)*** | 95 | | добавяне | ***O(logn)*** | 96 | | триене | ***O(logn)*** | 97 | 98 | | операция | масив | свързан списък | сортиран масив | двоично наредено дърво | балансирано дърво | 99 | | --- | --- | --- | --- | --- | --- | 100 | | търсене | ***O(n)*** | ***O(n)*** | ***O(logn)*** | ***O(n)*** | ***O(logn)*** | 101 | | добавяне | ***O(n)*** | ***O(n)*** | ***O(n)*** | ***O(n)*** | ***O(logn)*** | 102 | | триене | ***O(n)*** | ***O(n)*** | ***O(n)*** | ***O(n)*** | ***O(logn)*** | 103 | 104 | ## Задачи 105 | 106 | ### **Задача 0** 107 | Какво ще получим при **inorder** обхождане на двоично наредено дърво? 108 | 109 | ### **Задача 1** 110 | Към реализацията за двоично наредено дърво да се напише контсруктор, който приема масив и по него съставя двоично наредено дърво. 111 | 112 | ### **Задача 2** 113 | Да се напише функция, която проверява дали дадено двоично дърво е наредено. 114 | 115 | ### **Задача 3** 116 | Да се намери най-малкия общ предшественик на два възела в двоично наредено дърво. 117 | -------------------------------------------------------------------------------- /sem08/isBST.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | struct Node 7 | { 8 | T data; 9 | Node* left; 10 | Node* right; 11 | 12 | Node(const T& data, Node* left = nullptr, Node* right = nullptr) 13 | : data(data), left(left), right(right) {} 14 | }; 15 | 16 | // Задача 1 17 | bool isBSTNaive(Node* root) 18 | { 19 | if (root == nullptr || !root->left && !root->right) 20 | { 21 | return true; 22 | } 23 | 24 | if (root->left && !root->right) 25 | { 26 | return root->data > root->left->data && isBSTNaive(root->left); 27 | } 28 | else if (!root->left && root->right) 29 | { 30 | return root->data < root->right->data && isBSTNaive(root->left); 31 | } 32 | else 33 | { 34 | int rootValidity = root->data > root->left->data && root->data < root->right->data; 35 | return rootValidity && isBSTNaive(root->left) && isBSTNaive(root->right); 36 | } 37 | } 38 | 39 | // Задача 1 40 | bool isBSTVer1(Node* root, int minValue, int maxValue) 41 | { 42 | if (root == nullptr) 43 | { 44 | return true; 45 | } 46 | 47 | if (root->data < minValue || root->data > maxValue) 48 | { 49 | return false; 50 | } 51 | 52 | return isBSTVer1(root->left, minValue, root->data) 53 | && isBSTVer1(root->right, root->data, maxValue); 54 | } 55 | 56 | bool isBSTVer1(Node* root) 57 | { 58 | return isBSTVer1(root, INT_MIN, INT_MAX); 59 | } 60 | 61 | // Задача 1 62 | void isBSTVer2(Node* root, std::vector& elements) 63 | { 64 | if (root == nullptr) 65 | { 66 | return; 67 | } 68 | 69 | isBSTVer2(root->left, elements); 70 | elements.push_back(root->data); 71 | isBSTVer2(root->right, elements); 72 | } 73 | 74 | bool isBSTVer2(Node* root) 75 | { 76 | std::vector inorderTraversal; 77 | isBSTVer2(root, inorderTraversal); 78 | 79 | int size = inorderTraversal.size(); 80 | for (int i = 1; i < size; i++) 81 | { 82 | if (inorderTraversal[i - 1] >= inorderTraversal[i]) 83 | { 84 | return false; 85 | } 86 | } 87 | 88 | return true; 89 | } 90 | 91 | int main() 92 | { 93 | Node* root1 = new Node(12); 94 | root1->left = new Node(5); 95 | root1->left->left = new Node(3); 96 | root1->left->right = new Node(14); 97 | root1->right = new Node(20); 98 | root1->right->left = new Node(15); 99 | root1->right->right = new Node(28); 100 | 101 | Node* root = new Node(12); 102 | root->left = new Node(5); 103 | root->left->left = new Node(3); 104 | root->left->right = new Node(7); 105 | root->right = new Node(20); 106 | root->right->left = new Node(15); 107 | root->right->right = new Node(28); 108 | 109 | std::cout << isBSTNaive(root1) << '\n'; 110 | std::cout << isBSTVer1(root1) << '\n'; 111 | std::cout << isBSTVer2(root1) << '\n'; 112 | 113 | std::cout << isBSTNaive(root) << '\n'; 114 | std::cout << isBSTVer1(root) << '\n'; 115 | std::cout << isBSTVer2(root) << '\n'; 116 | } 117 | -------------------------------------------------------------------------------- /sem09/01-kth-smallest-and-largest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | Node* left; 9 | Node* right; 10 | 11 | Node(const T& data, Node* left = nullptr, Node* right = nullptr) 12 | : data(data), left(left), right(right) {} 13 | }; 14 | 15 | Node* kSmallestHelper(Node* root, int& i, int k) 16 | { 17 | if (root == nullptr) 18 | { 19 | return nullptr; 20 | } 21 | 22 | Node* left = kSmallestHelper(root->left, i, k); 23 | if (left) 24 | { 25 | return left; 26 | } 27 | 28 | i++; 29 | if (i == k) 30 | { 31 | return root; 32 | } 33 | 34 | return kSmallestHelper(root->right, i, k); 35 | } 36 | 37 | int kSmallest(Node* root, int k) 38 | { 39 | int i = 0; 40 | Node* node = kSmallestHelper(root, i, k); 41 | return node ? node->data : throw std::runtime_error("k exceeds tree nodes count"); 42 | } 43 | 44 | Node* kLargestHelper(Node* root, int& i, int k) 45 | { 46 | if (root == nullptr) 47 | { 48 | return nullptr; 49 | } 50 | 51 | Node* right = kLargestHelper(root->right, i, k); 52 | if (right) 53 | { 54 | return right; 55 | } 56 | 57 | i++; 58 | if (i == k) 59 | { 60 | return root; 61 | } 62 | 63 | return kLargestHelper(root->left, i, k); 64 | } 65 | 66 | int kLargest(Node* root, int k) 67 | { 68 | int i = 0; 69 | Node* node = kLargestHelper(root, i, k); 70 | return node ? node->data : throw std::runtime_error("k exceeds tree nodes count"); 71 | } 72 | 73 | int main() 74 | { 75 | Node* root = new Node(12); 76 | root->left = new Node(5); 77 | root->left->left = new Node(3); 78 | root->left->right = new Node(7); 79 | root->right = new Node(20); 80 | root->right->left = new Node(15); 81 | root->right->right = new Node(28); 82 | 83 | std::cout << kSmallest(root, 1) << '\n'; 84 | std::cout << kSmallest(root, 2) << '\n'; 85 | std::cout << kSmallest(root, 3) << '\n'; 86 | std::cout << kSmallest(root, 4) << '\n'; 87 | std::cout << kSmallest(root, 5) << '\n'; 88 | std::cout << kSmallest(root, 6) << '\n'; 89 | std::cout << kSmallest(root, 7) << '\n'; 90 | 91 | std::cout << kLargest(root, 7) << '\n'; 92 | std::cout << kLargest(root, 6) << '\n'; 93 | std::cout << kLargest(root, 5) << '\n'; 94 | std::cout << kLargest(root, 4) << '\n'; 95 | std::cout << kLargest(root, 3) << '\n'; 96 | std::cout << kLargest(root, 2) << '\n'; 97 | std::cout << kLargest(root, 1) << '\n'; 98 | } -------------------------------------------------------------------------------- /sem09/02-gca.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | Node* left; 9 | Node* right; 10 | 11 | Node(const T& data, Node* left = nullptr, Node* right = nullptr) 12 | : data(data), left(left), right(right) {} 13 | }; 14 | 15 | template 16 | Node* helper(Node* root, Node* x, Node* y) 17 | { 18 | if (root == nullptr) 19 | { 20 | return nullptr; 21 | } 22 | 23 | if (root->data > std::max(x->data, y->data)) 24 | { 25 | return helper(root->left, x, y); 26 | } 27 | else if (root->data < std::min(x->data, y->data)) 28 | { 29 | return helper(root->right, x, y); 30 | } 31 | 32 | return root; 33 | } 34 | 35 | template 36 | T getLeastCommonAncestor(Node* root, Node* x, Node* y) 37 | { 38 | Node* node = helper(root, x, y); 39 | return node ? node->data : throw std::runtime_error("exception"); 40 | } 41 | 42 | int main() 43 | { 44 | Node* root = new Node(12); 45 | root->left = new Node(5); 46 | root->left->left = new Node(3); 47 | root->left->right = new Node(7); 48 | root->right = new Node(20); 49 | root->right->left = new Node(15); 50 | root->right->right = new Node(28); 51 | 52 | std::cout << getLeastCommonAncestor(root, root->left->left, root->left->right) << '\n'; 53 | std::cout << getLeastCommonAncestor(root, root->right->left, root->right->right) << '\n'; 54 | std::cout << getLeastCommonAncestor(root, root->left, root->left->right) << '\n'; 55 | std::cout << getLeastCommonAncestor(root, root, root->left->right) << '\n'; 56 | std::cout << getLeastCommonAncestor(root, root->left->left, root->right) << '\n'; 57 | 58 | } -------------------------------------------------------------------------------- /sem09/03-sameBST.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | Node* left; 8 | Node* right; 9 | 10 | Node(const T& data, Node* left = nullptr, Node* right = nullptr) 11 | : data(data), left(left), right(right) {} 12 | }; 13 | 14 | template 15 | bool representSameBST(T* arr1, T* arr2, int n) 16 | { 17 | if (n == 0) 18 | { 19 | return true; 20 | } 21 | 22 | if (n == 1) 23 | { 24 | return arr1[0] == arr2[0]; 25 | } 26 | 27 | int size = n - 1; 28 | T leftSubtree1[size]; 29 | T rigthSubtree1[size]; 30 | T leftSubtree2[size]; 31 | T rigthSubtree2[size]; 32 | 33 | int leftSubtree1Index = 0, rigthSubtree1Index = 0, leftSubtree2Index = 0, rigthSubtree2Index = 0; 34 | 35 | for (int i = 1; i < n; i++) 36 | { 37 | if (arr1[i] < arr1[0]) 38 | { 39 | leftSubtree1[leftSubtree1Index++] = arr1[i]; 40 | } 41 | else 42 | { 43 | rigthSubtree1[rigthSubtree1Index++] = arr1[i]; 44 | } 45 | 46 | if (arr2[i] < arr2[0]) 47 | { 48 | leftSubtree2[leftSubtree2Index++] = arr2[i]; 49 | } 50 | else 51 | { 52 | rigthSubtree2[rigthSubtree2Index++] = arr2[i]; 53 | } 54 | } 55 | 56 | bool currentSubtrees = leftSubtree1Index == leftSubtree2Index 57 | && rigthSubtree1Index == rigthSubtree2Index; 58 | 59 | return currentSubtrees 60 | && representSameBST(leftSubtree1, leftSubtree2, leftSubtree1Index) 61 | && representSameBST(rigthSubtree1, rigthSubtree2, rigthSubtree1Index); 62 | } 63 | 64 | int main() 65 | { 66 | int n = 11; 67 | int X[] = { 15, 25, 20, 22, 30, 18, 10, 8, 9, 12, 6 }; 68 | int Y[] = { 15, 10, 12, 8, 25, 30, 6, 20, 18, 9, 22 }; 69 | 70 | int X1[] = { 15, 25, 20, 22, 30, 19, 10, 8, 9, 12, 6 }; 71 | int Y1[] = { 15, 10, 12, 8, 25, 30, 6, 20, 18, 9, 22 }; 72 | 73 | std::cout << representSameBST(X, Y, n) 74 | << ' ' 75 | << representSameBST(X1, Y1, n); 76 | } -------------------------------------------------------------------------------- /sem09/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 9 2 | # Задачи върху двоично наредено дърво 3 | 4 | ### **Задача 1** 5 | Да се намери k-тия най-малък и k-тия най-голям елемент в двоично наредено дърво. 6 | 7 | ### **Задача 2** 8 | Да се намери най-близкия общ предшественик на два възела в двоично наредено дърво при допускане, че подадените възли фигурират в дървото. 9 | 10 | ### **Задача 3** 11 | Да се провери дали два масива представят едно и също двоично наредено дърво без да ги строи. 12 | Приемаме, че възлите се добавят в дървото в реда на срещането им в масива. 13 | 14 | Пример: 15 | { 15, 25, 20, 22, 30, 18, 10, 8, 9, 12, 6 } 16 | { 15, 10, 12, 8, 25, 30, 6, 20, 18, 9, 22 } 17 | 18 | ![Problem example](../media/sem09/sem09-same-bst-example.png) 19 | 20 | # Балансирано двоично наредено дърво 21 | 22 | **Balanced Binary Search Tree** (BBST) е самобалансиращо се двоично наредено дърво. Такъв тип дървета се пренареждат, така че да поддържат логаритмична височина. С това се осигуряват по-бързи операции за добавяне и триене на елемент. 23 | 24 | | операция | двоично наредено дърво | балансирано двоично наредено дърво | 25 | | --- | --- | --- | 26 | | търсене | ***O(n)*** | ***O(logn)*** | 27 | | добавяне | ***O(n)*** | ***O(logn)*** | 28 | | триене | ***O(n)*** | ***O(logn)*** | 29 | 30 |
31 | 32 | Какви самобалансиращи се дървета има? 33 | - AVL tree - разликата във височината на лявото и дясното дете е най-много 1 34 | - B-tree - позволява повече от едно деца 35 | - Red-black tree - всеки възел пази допълнителна информация за *цвета* си 36 | - AA tree - вариация на red-black tree, която позволява добавянето на *червени* върхове само в дясното поддърво 37 | - Weight-balanced trees 38 | и т.н. 39 | 40 | ## AVL Tree 41 | 42 | 1.**Height-balanced tree**
43 | 2.Разликата във височината на лявото и дясното поддърво е по-малка или равна на 1.
44 | 3.Ако разликата стане по-голяма от 1 се прави ребалансиране. 45 | 46 | ![AVL tree](../media/sem09/sem09-avl-tree.png) 47 | 48 | ## Основни операции 49 | 50 | ### **Кога има нужда да се балансира дървото?**
51 | 1.Всеки връх има фактор за балансираност
52 | 53 | Нека за репрезентацията на дървото използваме следната структура: 54 | ```cpp 55 | template 56 | struct Node 57 | { 58 | T data; 59 | Node* left; 60 | Node* right; 61 | }; 62 | ``` 63 | 64 | Факторът за балансираност на дървото се смята по формулата:
65 | 66 | BF(node) = height(tree->right) - height(tree->left) 67 | 68 | За да бъде дървото AVL tree, трябва да бъде изпълнено, че BF ∈ {-1, 0, +1} за всеки възел от дървото. 69 | 70 | ### **Търсене** 71 | Тъй като търсенето не модифицира дървото, то няма как да промени свойството му за балансираност. 72 | Операцията по търсене не се различава от операцията по търсене в небалансирано двоично наредено дърво. 73 | 74 | ### **Ротация** 75 | 76 | ![Rotations](../media/sem09/sem09-rotation.png) 77 | 78 | Защо е валидно?
79 | Инвариантата остава в сила: n.left < n и n < n.right.
80 | ** Приемаме, че нямаме повтарящи се стойности 81 | 82 | ```cpp 83 | Node* rotateRight(Node* root) 84 | { 85 | Node* temp = root->left 86 | root->left = temp->right 87 | temp->right = root; 88 | 89 | return temp 90 | } 91 | ``` 92 | 93 | ![Rotations](../media/sem09/sem09-right-rotation.png) 94 | 95 | ⚠️ Ротация в поддърво 96 | 97 | Основни случаи 98 | 99 | Left left case: 100 | 101 | ![Left left case](../media/sem09/sem09-left-left-case.png) 102 | 103 | Left right case: 104 | 105 | ![Left right case](../media/sem09/sem09-left-right-case.png) 106 | 107 | Right right case: 108 | 109 | ![Right right case](../media/sem09/sem09-right-right-case.png) 110 | 111 | Right left case: 112 | 113 | ![Right left case](../media/sem09/sem09-right-left-case.png) 114 | 115 | ### **Добавяне** 116 | 117 | Добавяме само елементи, които не се съдържат в дървото. 118 | След стандартно добавяме проверяваме дали дървото има нужда да се ребалансира. 119 | 120 | ### **Триене** 121 | 122 | Аналогично на премахване на елемент от BST
123 | 1.) Намиране на елемента
124 | 125 | Премахване на:
126 | 1.1.) Листо - директно
127 | 1.2.) Връх с единствно ляво или дясно поддърво - пренасочване
128 | 1.3.) Връх с ляво и дясно поддърво - заместване на елемента с неговия следващ (successor)
129 | -------------------------------------------------------------------------------- /sem10/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 10 2 | # Графи. 3 | 4 | ## Видове графи 5 | - (не)ориентиран - ребрата имат ориентация(посока) 6 | - (не)тегловен - ребрата имат тегла (цени) 7 | - планарен - може да се нарисува в равнината, така че никои две ребра да не се пресичат 8 | - пълен - всеки два възела са свързани 9 | - свързан - съществува път между всеки два върха 10 | - обикновен/мултиграф 11 | 12 | ## Основни операции 13 | - добавяне/премахване на връх 14 | - добавяне/премахване на ребро 15 | - достъпване на наследниците на връх 16 | 17 | ## Представяния 18 | 19 | ### **1. Матрица на съседство** 20 |
21 | Нека M(nxn) е двоична матрица на съседство за граф G.
22 | M[i][j] представя реброто от връх i до връх j. Ако:
23 |
24 | - M[i][j] = 0, то ∄ (i, j)
25 | - M[i][j] = 1, то ∃ (i, j)
26 | - Може да представя теглови графи, като вместо 0/1 в клетката стои теглото на реброто

27 | 28 | | + | - | 29 | | --- | --- | 30 | | Подходящо за гъсти графи | Използва O(V^2) памет | 31 | | Намира дали има ребро между два върха в O(1) време | Итерацията през всички ребра отнема O(V^2) време | 32 | 33 | ### **2. Списъци на наследници / Списъци на съседство** 34 |
35 | Всеки връх има списък с върховете, до които може да стигне. 36 |

37 | 38 | | + | - | 39 | | --- | --- | 40 | | Подходящо за sparse графи | По-малко ефикасно за гъсти графи | 41 | | Итерирането през ребрата е ефикасно | Намира дали съществува ребро между два върха в O(E) време | 42 | 43 | ### **3. Списък на ребрата** 44 |
45 | Използват се наредени тройки от вида (начален връх, краен връх, тегло).

46 | 47 | | + | - | 48 | | --- | --- | 49 | | Подходяща за някои алгоритми | Няма добра структура | 50 | | Ефикасна за sparse графи | Намиране на ребро става в O(E) време | 51 | | Итерирането през ребрата е ефикасно | По-малко ефиксана реализация за пълни графи | 52 | 53 | ![Unoriented graph](../media/sem10/unoriented-graph.png) 54 |
55 |
56 | ![Oriented graph](../media/sem10/oriented-graph.png) 57 | 58 | ### Как избираме подходяща реализация? 59 | - Ориентиран ли е графа? 60 | - Ребрата имат ли тегла? 61 | - Много ребра ли ще има? 62 | 63 | # Задачи 64 | 65 | ### 1. Обхождане на граф 66 | В широчина: BFS
67 | В дълбочина: DFS 68 | 69 | ### 2. Минимално покриващо дърво 70 | ### 3. Най-къс път 71 | ### 4. Свързаност 72 | ### 5. Засичане на цикли с отрицателни тегла 73 | ### 6. Силно-свързани компоненти 74 | ### 7. Travelling Salesman Problem 75 | ### 8. Намиране на мостове 76 | ### 9. Намиране на свързващи върхове 77 | -------------------------------------------------------------------------------- /sem10/adjacency-list-graph/adjacency-list-graph.cpp: -------------------------------------------------------------------------------- 1 | #include "adjacency-list-graph.hpp" 2 | 3 | AdjListGraph::AdjListGraph(int vertices, bool oriented) 4 | : Graph(vertices, oriented), adjList(vertices, std::list()) {} 5 | 6 | int AdjListGraph::addVertex() 7 | { 8 | adjList.push_back(std::list()); 9 | return vertices++; 10 | } 11 | 12 | // 0 -> 2 // , 1 13 | // // 1 -> 5, 6 14 | // 1 15 | // 2 16 | // 3 -> 2, 4 17 | // 4 -> 18 | // 5 -> // 1 19 | 20 | void AdjListGraph::removeVertex(int vertex) 21 | { 22 | if (!existsVertex(vertex)) 23 | { 24 | throw std::runtime_error("Invalid vertrex"); 25 | } 26 | 27 | adjList.erase(adjList.begin() + vertex); 28 | vertices--; 29 | 30 | for (int i = 0; i < vertices; i++) 31 | { 32 | for (std::list::iterator it = adjList[i].begin(); it != adjList[i].end();) 33 | { 34 | if (it->end == vertex) 35 | { 36 | it = adjList[i].erase(it); 37 | } 38 | else if (it->end > vertex) 39 | { 40 | (it->end)--; 41 | it++; 42 | } 43 | else 44 | { 45 | it++; 46 | } 47 | } 48 | } 49 | } 50 | 51 | void AdjListGraph::addEdge(int start, int end, int weight) 52 | { 53 | if (!existsVertex(start) || !existsVertex(end)) 54 | { 55 | return; 56 | } 57 | 58 | adjList[start].push_back(Edge(end, weight)); 59 | 60 | if (!oriented) 61 | { 62 | adjList[end].push_back(Edge(end, weight)); 63 | } 64 | } 65 | 66 | void AdjListGraph::removeEdge(int start, int end) 67 | { 68 | if (!existsVertex(start) || !existsVertex(end)) 69 | { 70 | return; 71 | } 72 | 73 | for (std::list::iterator it = adjList[start].begin(); it != adjList[start].end();) 74 | { 75 | if (it->end == end) 76 | { 77 | it = adjList[start].erase(it); 78 | } 79 | else 80 | { 81 | it++; 82 | } 83 | } 84 | 85 | if (oriented) 86 | { 87 | return; 88 | } 89 | 90 | for (std::list::iterator it = adjList[end].begin(); it != adjList[end].end();) 91 | { 92 | if (it->end == start) 93 | { 94 | adjList[end].erase(it); 95 | } 96 | else 97 | { 98 | it++; 99 | } 100 | } 101 | } 102 | 103 | void AdjListGraph::getSuccessors(int vertex, std::vector>& successors) const 104 | { 105 | if (!existsVertex(vertex)) 106 | { 107 | throw std::runtime_error("Invalid vertex"); 108 | } 109 | 110 | for (auto i = adjList[vertex].begin(); i != adjList[vertex].end(); i++) 111 | { 112 | successors.push_back(std::make_pair(i->end, i->weight)); 113 | } 114 | } 115 | 116 | void AdjListGraph::getPredecessors(int vertex, std::vector>& predecessors) const 117 | { 118 | 119 | } 120 | 121 | bool AdjListGraph::adjacent(int start, int end) const 122 | { 123 | if (!existsVertex(start) || !existsVertex(end)) 124 | { 125 | return false; 126 | } 127 | 128 | for (auto i = adjList[start].begin(); i != adjList[start].end(); i++) 129 | { 130 | if (i->end == end) 131 | { 132 | return true; 133 | } 134 | } 135 | 136 | return false; 137 | } 138 | 139 | void AdjListGraph::getEdges(std::vector>& edges) const 140 | { 141 | for (int i = 0; i < adjList.size(); i++) 142 | { 143 | for(auto it = adjList[i].begin(); it != adjList[i].end(); it++) 144 | { 145 | edges.push_back(std::make_tuple(i, it->end, it->weight)); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /sem10/adjacency-list-graph/adjacency-list-graph.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _ADJACENCY_LIST_GRAPH_ 2 | #define _ADJACENCY_LIST_GRAPH_ 3 | 4 | #include "../graph.hpp" 5 | #include 6 | 7 | class AdjListGraph : public Graph 8 | { 9 | private: 10 | 11 | struct Edge 12 | { 13 | int end; 14 | int weight; 15 | 16 | Edge(int end, int weight = 1) 17 | : end(end), weight(weight) {} 18 | }; 19 | 20 | std::vector> adjList; 21 | 22 | public: 23 | AdjListGraph(int vertices, bool oriented); 24 | 25 | int addVertex() override; 26 | void removeVertex(int vertex) override; 27 | 28 | void addEdge(int start, int end, int weight = 1) override; 29 | void removeEdge(int start, int end) override; 30 | 31 | void getSuccessors(int vertex, std::vector>& successors) const override; 32 | void getPredecessors(int vertex, std::vector>& predecessors) const override; 33 | 34 | bool adjacent(int start, int end) const override; 35 | void getEdges(std::vector>& edges) const override; 36 | 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /sem10/adjacency-matrix-graph/adjacency-matrix-graph.cpp: -------------------------------------------------------------------------------- 1 | #include "adjacency-matrix-graph.hpp" 2 | 3 | AdjMatrixGraph::AdjMatrixGraph(int vertices, bool oriented) 4 | : Graph(vertices, oriented), matrix(vertices, std::vector(vertices, 0)) 5 | { 6 | 7 | } 8 | 9 | int AdjMatrixGraph::addVertex() 10 | { 11 | matrix.push_back(std::vector(vertices + 1, 0)); 12 | 13 | for (int i = 0; i < vertices; i++) 14 | { 15 | matrix[i].push_back(0); 16 | } 17 | 18 | return vertices++; 19 | } 20 | 21 | void AdjMatrixGraph::removeVertex(int vertex) 22 | { 23 | if (!existsVertex(vertex)) 24 | { 25 | throw std::runtime_error("Invalid vertex"); 26 | } 27 | 28 | // 0 ... n-1 29 | 30 | matrix.erase(matrix.begin() + vertex - 1); 31 | 32 | for (std::vector>::iterator it = matrix.begin(); it != matrix.end(); it++) 33 | { 34 | it->erase(it->begin() + vertex); 35 | } 36 | 37 | vertices--; 38 | } 39 | 40 | void AdjMatrixGraph::addEdge(int start, int end, int weight) 41 | { 42 | if (!existsVertex(start) || !existsVertex(end)) 43 | { 44 | throw std::runtime_error("Invalid vertex"); 45 | } 46 | 47 | if (matrix[start][end] != 0) 48 | { 49 | throw std::runtime_error("Creating an existing edge"); 50 | } 51 | 52 | matrix[start][end] = weight; 53 | 54 | if (!oriented) 55 | { 56 | matrix[end][start] = weight; 57 | } 58 | } 59 | 60 | void AdjMatrixGraph::removeEdge(int start, int end) 61 | { 62 | if (!existsVertex(start) || !existsVertex(end)) 63 | { 64 | throw std::runtime_error("Invalid vertex"); 65 | } 66 | 67 | matrix[start][end] = 0; 68 | if (!oriented) 69 | { 70 | matrix[end][start] = 0; 71 | } 72 | } 73 | 74 | void AdjMatrixGraph::getSuccessors(int vertex, std::vector>& successors) const 75 | { 76 | if (!existsVertex(vertex)) 77 | { 78 | throw std::runtime_error("Invalid vertex"); 79 | } 80 | 81 | for (int i = 0; i < matrix[vertex].size(); i++) 82 | { 83 | if (matrix[vertex][i] > 0) 84 | { 85 | successors.push_back(std::make_pair(i, matrix[vertex][i])); 86 | } 87 | } 88 | } 89 | 90 | void AdjMatrixGraph::getPredecessors(int vertex, std::vector>& predecessors) const 91 | { 92 | if (!existsVertex(vertex)) 93 | { 94 | throw std::runtime_error("Invalid vertex"); 95 | } 96 | 97 | for (int i = 0; i < matrix[vertex].size(); i++) 98 | { 99 | if (matrix[i][vertex] > 0) 100 | { 101 | predecessors.push_back(std::make_pair(i, matrix[i][vertex])); 102 | } 103 | } 104 | } 105 | 106 | bool AdjMatrixGraph::adjacent(int start, int end) const 107 | { 108 | if (!existsVertex(start) || !existsVertex(end)) 109 | { 110 | throw std::runtime_error("Invalid vertex"); 111 | } 112 | 113 | return matrix[start][end]; 114 | } 115 | 116 | void AdjMatrixGraph::getEdges(std::vector>& edges) const 117 | { 118 | for (int i = 0; i < vertices; i++) 119 | { 120 | for (int j = 0; j < vertices; j++) 121 | { 122 | if (matrix[i][j] != 0) 123 | { 124 | edges.push_back(std::make_tuple(i, j, matrix[i][j])); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /sem10/adjacency-matrix-graph/adjacency-matrix-graph.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _ADJACENCY_MATRIX_GRAPH_ 2 | #define _ADJACENCY_MATRIX_GRAPH_ 3 | 4 | #include "../graph.hpp" 5 | 6 | class AdjMatrixGraph : public Graph 7 | { 8 | private: 9 | std::vector> matrix; 10 | 11 | public: 12 | AdjMatrixGraph(int vertices, bool oriented); 13 | 14 | int addVertex() override; 15 | void removeVertex(int vertex) override; 16 | 17 | void addEdge(int start, int end, int weight = 1) override; 18 | void removeEdge(int start, int end) override; 19 | 20 | void getSuccessors(int vertex, std::vector>& successors) const override; 21 | void getPredecessors(int vertex, std::vector>& predecessors) const override; 22 | 23 | bool adjacent(int start, int end) const override; 24 | void getEdges(std::vector>& edges) const override; 25 | 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /sem10/algorithms.cpp: -------------------------------------------------------------------------------- 1 | #include "algorithms.hpp" 2 | 3 | void BFS(const Graph& g, int start, std::vector& order) 4 | { 5 | if (!g.existsVertex(start)) 6 | { 7 | return; 8 | } 9 | 10 | std::queue q; 11 | std::vector visited(g.getVerticesCount()); 12 | 13 | q.push(start); 14 | visited[start] = true; 15 | 16 | while (!q.empty()) 17 | { 18 | int current = q.front(); 19 | q.pop(); 20 | 21 | // order.push_back(current); 22 | 23 | std::vector> successors; 24 | g.getSuccessors(current, successors); 25 | 26 | for (int i = 0; i < successors.size(); i++) 27 | { 28 | if (visited[successors[i].first]) 29 | { 30 | continue; 31 | } 32 | 33 | visited[successors[i].first] = true; 34 | q.push(successors[i].first); 35 | } 36 | } 37 | } 38 | 39 | void DFS(const Graph& g, int start, std::vector& order) 40 | { 41 | if (!g.existsVertex(start)) 42 | { 43 | return; 44 | } 45 | 46 | std::stack st; 47 | std::vector visited(g.getVerticesCount()); 48 | 49 | st.push(start); 50 | 51 | while (!st.empty()) 52 | { 53 | int current = st.top(); 54 | st.pop(); 55 | 56 | if (visited[current]) 57 | { 58 | continue; 59 | } 60 | 61 | visited[current] = true; 62 | 63 | // order.push_back(current); 64 | 65 | std::vector> successors; 66 | g.getSuccessors(current, successors); 67 | 68 | for (int i = 0; i < successors.size(); i++) 69 | { 70 | st.push(successors[i].first); 71 | } 72 | } 73 | } 74 | 75 | void DFS_rec(const Graph& g, int start, std::vector& visited) 76 | { 77 | visited[start] = true; 78 | 79 | std::vector> successors; 80 | g.getSuccessors(start, successors); 81 | 82 | for (int i = 0; i < successors.size(); i++) 83 | { 84 | if (!visited[successors[i].first]) 85 | { 86 | DFS_rec(g, successors[i].first, visited); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /sem10/algorithms.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _GRAPH_ALGORITHMS_ 2 | #define _GRAPH_ALGORITHMS_ 3 | 4 | // BFS - breadth first search 5 | 6 | // DFS - depth first search 7 | 8 | #include "graph.hpp" 9 | #include 10 | #include 11 | #include 12 | 13 | void BFS(const Graph& g, int start, std::vector& order); 14 | void DFS(const Graph& g, int start, std::vector& order); 15 | void DFS_rec(const Graph& g, int start, std::vector& visited); 16 | 17 | #endif -------------------------------------------------------------------------------- /sem10/edge-list-graph/edge-list-graph.cpp: -------------------------------------------------------------------------------- 1 | #include "edge-list-graph.hpp" 2 | 3 | EdgeListGraph::EdgeListGraph(int n, bool oriented) 4 | : Graph(n, oriented) {} 5 | 6 | int EdgeListGraph::addVertex() 7 | { 8 | vertices++; 9 | return vertices - 1; 10 | } 11 | 12 | void EdgeListGraph::removeVertex(int vertex) 13 | { 14 | if (!existsVertex(vertex)) 15 | { 16 | throw std::runtime_error("Invalid vertex"); 17 | } 18 | 19 | //remove edges to/from vertex 20 | for (auto it = edges.begin(); it != edges.end();) 21 | { 22 | if (it->start == vertex || it->end == vertex) 23 | { 24 | edges.erase(it); 25 | continue; 26 | } 27 | 28 | if (it->start > vertex) 29 | { 30 | it->start--; 31 | } 32 | if (it->end > vertex) 33 | { 34 | it->end--; 35 | } 36 | it++; 37 | } 38 | 39 | vertices--; 40 | } 41 | 42 | void EdgeListGraph::addEdge(int start, int end, int weight) 43 | { 44 | if (!existsVertex(start) || !existsVertex(end)) 45 | { 46 | throw std::runtime_error("Invalid index"); 47 | } 48 | 49 | edges.push_back({ start, end, weight }); 50 | 51 | if (!oriented) 52 | { 53 | edges.push_back({ end, start, weight }); 54 | } 55 | } 56 | 57 | void EdgeListGraph::removeEdge(int start, int end) 58 | { 59 | if (!existsVertex(start) || !existsVertex(end)) 60 | { 61 | throw std::runtime_error("Invalid index"); 62 | } 63 | 64 | for (auto it = edges.begin(); it != edges.end();) 65 | { 66 | if ((it->start == start && it->end == end) || (!oriented && it->start == end && it->end == start)) 67 | { 68 | it = edges.erase(it); 69 | } 70 | else 71 | { 72 | it++; 73 | } 74 | } 75 | } 76 | 77 | void EdgeListGraph::getSuccessors(int vertex, std::vector>& successors) const 78 | { 79 | if (!existsVertex(vertex)) 80 | { 81 | throw std::runtime_error("Invalid index"); 82 | } 83 | 84 | for (auto it = edges.begin(); it != edges.end(); it++) 85 | { 86 | if (it->start == vertex) 87 | { 88 | successors.push_back(std::make_pair(it->end, it->weight)); 89 | } 90 | } 91 | } 92 | 93 | void EdgeListGraph::getPredecessors(int vertex, std::vector>& predecessors) const 94 | { 95 | if (!existsVertex(vertex)) 96 | { 97 | throw std::runtime_error("Invalid index"); 98 | } 99 | 100 | for (auto it = edges.begin(); it != edges.end(); it++) 101 | { 102 | if (it->end == vertex) 103 | { 104 | predecessors.push_back(std::make_pair(it->start, it->weight)); 105 | } 106 | } 107 | } 108 | 109 | bool EdgeListGraph::adjacent(int start, int end) const 110 | { 111 | if (!existsVertex(start) || !existsVertex(end)) 112 | { 113 | throw std::runtime_error("Invalid vertex"); 114 | } 115 | 116 | for (auto it = edges.begin(); it != edges.end(); it++) 117 | { 118 | if (it->start == start && it->end == end) 119 | { 120 | return true; 121 | } 122 | } 123 | 124 | return false; 125 | } 126 | 127 | void EdgeListGraph::getEdges(std::vector>& edges) const 128 | { 129 | for (auto it = this->edges.begin(); it != this->edges.end(); it++) 130 | { 131 | edges.push_back(std::make_tuple(it->start, it->end, it->weight)); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /sem10/edge-list-graph/edge-list-graph.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _EDGE_LIST_GRAPH_ 2 | #define _EDGE_LIST_GRAPH_ 3 | 4 | #include "../graph.hpp" 5 | 6 | class EdgeListGraph : public Graph 7 | { 8 | private: 9 | 10 | struct Edge 11 | { 12 | int start; 13 | int end; 14 | int weight; 15 | }; 16 | 17 | std::vector edges; 18 | 19 | public: 20 | 21 | EdgeListGraph(int vertices, bool oriented); 22 | 23 | int addVertex() override; 24 | void removeVertex(int vertex) override; 25 | 26 | void addEdge(int start, int end, int weight = 1) override; 27 | void removeEdge(int start, int end) override; 28 | 29 | void getSuccessors(int vertex, std::vector>& successors) const override; 30 | void getPredecessors(int vertex, std::vector>& predecessors) const override; 31 | 32 | bool adjacent(int start, int end) const override; 33 | void getEdges(std::vector>& edges) const override; 34 | 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /sem10/graph.cpp: -------------------------------------------------------------------------------- 1 | #include "graph.hpp" 2 | 3 | Graph::Graph(int vertices, bool oriented) 4 | : vertices(vertices), oriented(oriented) {} 5 | 6 | bool Graph::isOriented() const 7 | { 8 | return oriented; 9 | } 10 | 11 | int Graph::getVerticesCount() const 12 | { 13 | return vertices; 14 | } 15 | 16 | bool Graph::existsVertex(int vertex) const 17 | { 18 | return vertex >= 0 && vertex < vertices; 19 | } 20 | -------------------------------------------------------------------------------- /sem10/graph.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _GRAPH_ 2 | #define _GRAPH_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class Graph 9 | { 10 | protected: 11 | int vertices; 12 | bool oriented; 13 | 14 | public: 15 | 16 | Graph(int vertices, bool oriented); 17 | 18 | virtual int addVertex() = 0; 19 | virtual void removeVertex(int vertex) = 0; 20 | 21 | virtual void addEdge(int start, int end, int weight = 1) = 0; 22 | virtual void removeEdge(int start, int end) = 0; 23 | 24 | virtual void getSuccessors(int vertex, std::vector>& successors) const = 0; 25 | virtual void getPredecessors(int vertex, std::vector>& predecessors) const = 0; 26 | 27 | virtual bool adjacent(int start, int end) const = 0; 28 | virtual void getEdges(std::vector>& edges) const = 0; 29 | 30 | bool isOriented() const; 31 | int getVerticesCount() const; 32 | bool existsVertex(int vertex) const; 33 | 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /sem10/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "./adjacency-list-graph/adjacency-list-graph.hpp" 3 | #include "./edge-list-graph/edge-list-graph.hpp" 4 | #include "./adjacency-matrix-graph/adjacency-matrix-graph.hpp" 5 | 6 | void printTupleEdge(const std::tuple& edge) 7 | { 8 | std::cout << "(" << std::get<0>(edge) 9 | << ", " << std::get<1>(edge) 10 | << ", " << std::get<2>(edge) 11 | << ")"; 12 | } 13 | 14 | void printEdges(const std::vector>& edges) 15 | { 16 | for (int i = 0; i < edges.size(); i++) 17 | { 18 | printTupleEdge(edges[i]); 19 | std::cout << '\n'; 20 | } 21 | } 22 | 23 | int main() 24 | { 25 | // AdjListGraph g(5, 1); 26 | // AdjMatrixGraph g(5, 1); 27 | EdgeListGraph g(5, 1); 28 | std::cout << "Vertices count: " << g.getVerticesCount() << '\n'; 29 | 30 | g.addEdge(0, 2, 1); 31 | g.addEdge(0, 4, 4); 32 | g.addEdge(3, 2, 2); 33 | g.addEdge(4, 2, 3); 34 | 35 | std::cout << "Add vertex: " << g.addVertex() << '\n'; 36 | std::cout << "Add vertex: " << g.addVertex() << '\n'; 37 | std::cout << "Vertices count: " << g.getVerticesCount() << '\n'; 38 | 39 | g.removeVertex(3); 40 | std::cout << "Vertices count: " << g.getVerticesCount() << '\n'; 41 | 42 | // g.removeEdge(0, 2); 43 | 44 | std::vector> edges; 45 | g.getEdges(edges); 46 | printEdges(edges); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /sem11/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 11 2 | # Приоритетна опашка. 3 | 4 | ## Приоритетна опашка 5 | 6 | - Абстрактен тип данни, подобен на опашка, но всеки елемент има приоритет 7 | - Приоритетът определя реда на премахване на елментите от опашката 8 | - Елементите на приоритетна опашка трябва да са сравними 9 | - При наличие на повече елементи с един и същи приоритет се прилага принципа FIFO 10 | 11 | **Основни операции** 12 | - Добавяне на елемент 13 | - Триене на елемент според приоритета (най-висок приоритет или най-нисък приоритет) 14 | 15 | **Имплементации** 16 | 17 | | Операция | Свързан списък | Сортиран свързан списък | BST | 18 | | --- | --- | --- | --- | 19 | | Добавяне | ***O(1)*** | ***O(n)*** | ***O(logn)*** | 20 | | Триене | ***O(n)*** | ***O(1)*** | ***O(logn)*** | 21 | 22 | **Приложения** 23 | - в операционните системи (за организация за подреждане на процеси за изпълнение) 24 | - сортиране 25 | - алгоритми - алгоритъм за най-къс път на Dijkstra, алчни алгоритми (greedy algorithms) и т.н. 26 | 27 | ## Priority Queue ≠ Binary Heap 28 | **Пълнота** - binary heap представлява попълнено двоично дърво (всички нива са попълнени с възможно изключение за последното - то се запълва отляво надясно) 29 | **Наредба** - всеки връх е по-малък/по-голям или равен на наследниците си 30 | 31 | Min heap 32 | 33 | ![Min heap](../media/sem11/sem11-min-heap.png) 34 | 35 | Max heap 36 | 37 | ![Max heap](../media/sem11/sem11-max-heap.png) 38 | -------------------------------------------------------------------------------- /sem11/priority-queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | class PriorityQueue 7 | { 8 | struct Node 9 | { 10 | T data; 11 | int ts; 12 | 13 | Node(T data, int ts) : data(data), ts(ts) {} 14 | 15 | bool operator<(const Node& other) const 16 | { 17 | if (data < other.data) 18 | { 19 | return true; 20 | } 21 | 22 | if (data == other.data) 23 | { 24 | return ts < other.ts; 25 | } 26 | 27 | return false; 28 | } 29 | }; 30 | 31 | std::vector data; 32 | int ts = 0; 33 | 34 | private: 35 | 36 | void copy(const PriorityQueue& other); 37 | void free(); 38 | 39 | int leftChild(int i) const; 40 | int rightChild(int i) const; 41 | int parent(int i) const; 42 | 43 | void heapify(int index); 44 | void print(int i) const; 45 | 46 | public: 47 | PriorityQueue() = default; 48 | PriorityQueue(const std::vector& v); 49 | PriorityQueue(const PriorityQueue& other); 50 | PriorityQueue& operator=(const PriorityQueue& other); 51 | ~PriorityQueue(); 52 | 53 | void push(T element); 54 | void pop(); 55 | 56 | const T& top() const; 57 | bool empty() const; 58 | int getSize() const; 59 | void print() const; 60 | 61 | }; 62 | 63 | template 64 | void PriorityQueue::copy(const PriorityQueue& other) 65 | { 66 | ts = other.ts; 67 | 68 | for (int i = 0; i < other.data.size(); i++) 69 | { 70 | data.push_back(new Node(other.data[i])); 71 | } 72 | } 73 | 74 | template 75 | void PriorityQueue::free() 76 | { 77 | for (int i = 0; i < data.size(); i++) 78 | { 79 | delete data[i]; 80 | } 81 | 82 | ts = 0; 83 | } 84 | 85 | template 86 | PriorityQueue::PriorityQueue(const std::vector& v) 87 | { 88 | int size = v.size(); 89 | for (int i = 0; i < size; i++) 90 | { 91 | push(v[i]); 92 | // data.push_back(new Node(v[i], ts++)); 93 | } 94 | 95 | // to do 96 | // rearrange elements 97 | // heapify() 98 | } 99 | 100 | template 101 | PriorityQueue::PriorityQueue(const PriorityQueue& other) 102 | { 103 | copy(other); 104 | } 105 | 106 | template 107 | PriorityQueue& PriorityQueue::operator=(const PriorityQueue& other) 108 | { 109 | if (this != &other) 110 | { 111 | free(); 112 | copy(other); 113 | } 114 | return *this; 115 | } 116 | 117 | template 118 | PriorityQueue::~PriorityQueue() 119 | { 120 | free(); 121 | } 122 | 123 | // 0 | 1 2 | 3 4 | 5 6 | 7 124 | 125 | template 126 | int PriorityQueue::leftChild(int i) const 127 | { 128 | return 2*i + 1; 129 | } 130 | 131 | template 132 | int PriorityQueue::rightChild(int i) const 133 | { 134 | return 2*i + 2; 135 | } 136 | 137 | template 138 | int PriorityQueue::parent(int i) const 139 | { 140 | return (i - 1) / 2; 141 | } 142 | 143 | template 144 | void PriorityQueue::heapify(int index) 145 | { 146 | bool hasMoves = true; 147 | 148 | while(hasMoves) 149 | { 150 | int left = leftChild(index); 151 | int right = rightChild(index); 152 | 153 | bool toLeft = left < data.size() && *data[index] < *data[left]; 154 | bool toRight = right < data.size() && *data[index] < *data[right]; 155 | 156 | if (!toLeft && !toRight) 157 | { 158 | hasMoves = false; 159 | } 160 | else if (toLeft && !toRight) 161 | { 162 | std::swap(data[index], data[left]); 163 | index = left; 164 | } 165 | else if (!toLeft && toRight) 166 | { 167 | std::swap(data[index], data[right]); 168 | index = right; 169 | } 170 | else 171 | { 172 | if (*data[left] < *data[right]) 173 | { 174 | std::swap(data[index], data[right]); 175 | index = right; 176 | } 177 | else 178 | { 179 | std::swap(data[index], data[left]); 180 | index = left; 181 | } 182 | } 183 | } 184 | } 185 | 186 | 187 | template 188 | void PriorityQueue::push(T element) 189 | { 190 | Node* newNode = new Node(element, ts++); 191 | data.push_back(newNode); 192 | 193 | int index = data.size() - 1; 194 | int parentIndex = parent(index); 195 | 196 | while (index > 0 && *data[parentIndex] < *data[index]) 197 | { 198 | std::swap(data[index], data[parentIndex]); 199 | index = parentIndex; 200 | parentIndex = parent(index); 201 | } 202 | } 203 | 204 | template 205 | void PriorityQueue::pop() 206 | { 207 | if (data.size() == 0) 208 | { 209 | return; 210 | } 211 | 212 | if (data.size() == 1) 213 | { 214 | delete data[0]; 215 | ts = 0; 216 | return; 217 | } 218 | 219 | delete data[0]; 220 | data[0] = data[data.size() - 1]; 221 | data.pop_back(); 222 | 223 | heapify(0); 224 | } 225 | 226 | template 227 | const T& PriorityQueue::top() const 228 | { 229 | if (data.size() == 0) 230 | { 231 | throw std::runtime_error("Top called on an empty priority queue."); 232 | } 233 | 234 | return data[0]->data; 235 | } 236 | 237 | template 238 | bool PriorityQueue::empty() const 239 | { 240 | return data.empty(); 241 | } 242 | 243 | template 244 | int PriorityQueue::getSize() const 245 | { 246 | return data.size(); 247 | } 248 | 249 | template 250 | void PriorityQueue::print(int i) const 251 | { 252 | if (i >= data.size()) 253 | { 254 | return; 255 | } 256 | 257 | std::cout << '('; 258 | print(leftChild(i)); 259 | std::cout << data[i]->data; 260 | print(rightChild(i)); 261 | std::cout << ')'; 262 | } 263 | 264 | template 265 | void PriorityQueue::print() const 266 | { 267 | print(0); 268 | } 269 | 270 | int main() 271 | { 272 | std::vector v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 273 | PriorityQueue pq(v); 274 | pq.print(); 275 | std::cout << "\n\n"; 276 | 277 | pq.push(100); 278 | std::cout << "Insert new biggest element 100:\n"; 279 | pq.print(); 280 | std::cout << "\n\n"; 281 | 282 | std::cout << "Pop:\n"; 283 | pq.pop(); 284 | pq.print(); 285 | } -------------------------------------------------------------------------------- /sem12/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 12 2 | # Алгоритми върху графи. 3 | 4 | ## Търсене на броя на свързаните компоненти в граф 5 | 6 | ![Connected components](../media/sem12/sem12-connected-components.png) 7 | 8 | ## Контролно (2020) 9 | Нека за неориентиран граф G имаме n на брой върхове, зададени чрез техния номер, и m на брой ребра. Освен това е даден масив topValues с n елемента, в който за всеки връх се пази съответна стойност, с която този връх се асоциира, т.е. в topValues[i] се пази стойност, която съответства на i+1-вия връх в графа. Масивът topValues е независим от представянето и дефинирането на графа. Реализирайте функция, която намира сумата от стойностите на онези върхове в G, които се явяват минимални в рамките на компонентата за свързаност, на която принадлежат.
10 | (Заб: Ако един връх не е свързан с нито един друг връх, то той се брои като самостоятелна компонента на свързаност.) 11 | 12 | ## Намиране на най-къс път (Dijkstra) 13 | Изискване: **неотрицателни** тегла на ребрата
14 | Сложност: ***O(E\*logV)***
15 | 16 | **Елементи:** 17 | - Вектор за разстоянията от началния връх до всеки друг (по начало +inf) 18 | - Приоритетна опашка за възлите и разстоянието до тях - дава следващия връх, който трябва да бъде посетен (определя се по най-лекото ребро) 19 | - Опционално: за извличане на пътя - масив, който пази информация за всеки връх от кой връх е бил достигнат (по начало -1) 20 | 21 | **Стъпки:** 22 | - Добавяме (s, 0) в приоритетната опашка и въртим цикъл докато тя не е празна 23 | - Итерираме през всички ребра навън спрямо текущото и релаксираме ребрата 24 | - Оптимизация: за намиране на най-къс път между два върха? 25 | 26 | ![Dijkstra](../media/sem12/sem12-dijkstra-algorithm.png) 27 | 28 | ## Търсене на цикъл в граф 29 | 30 | ## Топологическо сортиране 31 | Мотивация: много реални сценарии от ежедневието ни могат да бъдат моделирани с ориентиран граф, така че да изискваме дадени събития да се случат преди други 32 | 33 | Алгоритъм, който подрежда върховете на граф, така че в подредбата им за всяко срещане на връх i преди връх j, то не съществува ребро (j, i). С други думи няма ребра, които да сочат "назад" в наредбата на върховете. 34 | 35 | Топологическото сортиране не е уникално. 36 | 37 | Сложност: ***O(E+V)***
38 | 39 | Изисквания: Графът трябва да е ДАГ (directed acyclic graph). Защо? Какво правим, ако има цикъл? 40 | Алгоритъм: 41 | 1. Старитраме от непосетен връх 42 | 2. Обхождаме в дълбочина непосетените върхове 43 | 3. Добавяме обхождания връх в реда 44 | 4. Ако има непосетени върхове се връщаме на т.1 45 | 5. Обръщаме масива, в който сме запазили реда (алтернативно добавяме стойностите в обратен ред) 46 | 47 | ![Topological sort](../media/sem12/sem12-topological-sort.png) 48 | -------------------------------------------------------------------------------- /sem12/algorithms.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _GRAPH_ALGORITHMS_ 2 | #define _GRAPH_ALGORITHMS_ 3 | 4 | #include "../sem10/graph.hpp" 5 | #include 6 | #include 7 | #include 8 | 9 | enum Color { WHITE, GRAY, BLACK }; 10 | 11 | void connectedComponents(const Graph& g, int start, std::vector& visited); 12 | int getConnectedComponentsCount(const Graph& g); 13 | 14 | int minVertexBFS(const Graph& g, int start, std::vector& visited, const std::vector& topValues); 15 | int minValueOfConnectedComponents(const Graph& g, const std::vector& topValues); 16 | 17 | void Dijkstra(const Graph& g, int start, std::vector& dist, std::vector& prev); 18 | 19 | bool containsCycleRecursiveCallVer1(const Graph& g, int start, std::vector& colors); 20 | bool containsCycleRecursiveCallVer2(const Graph& g, int start, std::vector& visited, std::vector& st); 21 | bool containsCycleVer1(const Graph& g); 22 | bool containsCycleVer2(const Graph& g); 23 | 24 | void topoSortDFS(const Graph& g, int start, std::vector& visited); 25 | void topologicalSort(const Graph& g, std::vector& order); 26 | 27 | # endif -------------------------------------------------------------------------------- /sem12/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../sem10/graph.hpp" 3 | #include "../sem10/adjacency-list-graph/adjacency-list-graph.hpp" 4 | #include "../sem10/edge-list-graph/edge-list-graph.hpp" 5 | #include "../sem10/adjacency-matrix-graph/adjacency-matrix-graph.hpp" 6 | #include "algorithms.hpp" 7 | 8 | void printTupleEdge(const std::tuple& edge) 9 | { 10 | std::cout << "(" << std::get<0>(edge) 11 | << ", " << std::get<1>(edge) 12 | << ", " << std::get<2>(edge) 13 | << ")"; 14 | } 15 | 16 | void printEdges(const std::vector>& edges) 17 | { 18 | for (int i = 0; i < edges.size(); i++) 19 | { 20 | printTupleEdge(edges[i]); 21 | std::cout << '\n'; 22 | } 23 | } 24 | 25 | int main() 26 | { 27 | EdgeListGraph g(9, 1); 28 | 29 | g.addEdge(0, 1, 2); 30 | g.addEdge(0, 2, 3); 31 | g.addEdge(1, 2, 1); 32 | g.addEdge(1, 3, 4); 33 | g.addEdge(2, 3, 2); 34 | g.addEdge(2, 4, 5); 35 | g.addEdge(3, 5, 10); 36 | g.addEdge(4, 3, 4); 37 | g.addEdge(5, 6, 9); 38 | g.addEdge(5, 7, 11); 39 | g.addEdge(6, 7, 14); 40 | g.addEdge(7, 8, 5); 41 | 42 | std::vector> edges; 43 | g.getEdges(edges); 44 | printEdges(edges); 45 | 46 | std::cout << "Connected components: " << getConnectedComponentsCount(g) << std::endl; 47 | std::cout << "Contains cycle: " << containsCycleVer1(g) << std::endl; 48 | 49 | std::vector dist(g.getVerticesCount()); 50 | std::vector prev(g.getVerticesCount()); 51 | 52 | Dijkstra(g, 0, dist, prev); 53 | for (int i = 0; i < g.getVerticesCount(); i++) 54 | { 55 | std::cout << "Distance 0-" << i << ": " << dist[i] << std::endl; 56 | } 57 | 58 | EdgeListGraph g1(9, 1); 59 | 60 | g1.addEdge(1, 2, 1); 61 | g1.addEdge(1, 3, 4); 62 | g1.addEdge(2, 3, 2); 63 | g1.addEdge(3, 5, 10); 64 | g1.addEdge(5, 6, 9); 65 | g1.addEdge(5, 7, 11); 66 | g1.addEdge(6, 7, 14); 67 | 68 | std::cout << "Connected components: " << getConnectedComponentsCount(g1) << std::endl; 69 | 70 | std::vector order; 71 | topologicalSort(g, order); 72 | 73 | std::cout << "Topological sort: "; 74 | for (int i = 0; i < order.size(); i++) 75 | { 76 | std::cout << order[i] << ' '; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /sem13/README.md: -------------------------------------------------------------------------------- 1 | # Упражнение № 13 2 | # Задачи върху дървета и графи. 3 | 4 | ### Задача 1. (Контролно 2021) 5 | Нека е дадено двоично дърво от цели числа с възли, представени със следната структура: 6 | 7 | struct Node {int data, Node* left, Node* right;}; 8 | 9 | "Растящ път" в непразно дърво наричаме всеки път от корена на дървото до негово листо, такъв че стойностите на последователните елементи от пътя нарастват. Да се дефинира функция int maxpath(Node* t), където t е корен на непразно двоично дърво. Да се намери най-голямото число, което може да се получи при сумиране на елементите на някой нарастващ път от дървото.
10 | 11 | Пример: ((\*3\*)2*)1((\*9\*)1(\*8\*))
12 | Най-голям растящ път: 1->2->3 : 6
13 | 14 | ### Задача 2. (Контролно 2021) 15 | Нека е дадено двоично дърво. "Десен изглед" на дървото наричаме най-десните елементи от всяко от нивата на дървото, взети в ред от корена към листата (все едно гледаме дървото от дясната му страна). 16 | 17 | Пример: ((\*2(\*5\*))1(\*3\*))
18 | Десен изглед: 1,3,5

19 | При представянето от предишната задача да се дефинира функция rightLook, която по корен на двоично дърво връща неговия десен изглед под формата на вектор или масив от числа.
20 | 21 | ### Задача 3. 22 | Да се напише функция, която проверява дали двоично дърво погледнато от ляво е същото като погледнато от дясно. 23 | -------------------------------------------------------------------------------- /sem13/binary-tree-paths.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct CharNode 5 | { 6 | char data; 7 | CharNode* left; 8 | CharNode* right; 9 | 10 | CharNode(char data) 11 | : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | void binaryTreePaths(CharNode* root, std::vector& paths) 15 | { 16 | if (root == nullptr) 17 | { 18 | return; 19 | } 20 | 21 | else if (root->left == nullptr && root->right == nullptr) 22 | { 23 | std::string str; 24 | str.push_back(root->data); 25 | paths.push_back(str); 26 | } 27 | 28 | std::vector left; 29 | binaryTreePaths(root->left, left); 30 | for (int i = 0; i < left.size(); ++i) 31 | { 32 | std::string current = root->data + left[i]; 33 | left[i] = current; 34 | } 35 | 36 | std::vector right; 37 | binaryTreePaths(root->right, right); 38 | for (int i = 0; i < right.size(); ++i) 39 | { 40 | std::string current = root->data + right[i]; 41 | right[i] = current; 42 | } 43 | 44 | for (std::string x : left) 45 | { 46 | paths.push_back(x); 47 | } 48 | for (std::string x : right) 49 | { 50 | paths.push_back(x); 51 | } 52 | } 53 | 54 | int main() 55 | { 56 | // a 57 | // / \ 58 | // b f 59 | // / \ / \ 60 | // c d g h 61 | // / 62 | // e 63 | 64 | CharNode* root = new CharNode('a'); 65 | root->left = new CharNode('b'); 66 | root->left->left = new CharNode('c'); 67 | root->left->right = new CharNode('d'); 68 | root->left->right->left = new CharNode('e'); 69 | root->right = new CharNode('f'); 70 | root->right->left = new CharNode('g'); 71 | root->right->right = new CharNode('h'); 72 | 73 | std::vector paths; 74 | binaryTreePaths(root, paths); 75 | 76 | for (int i = 0; i < paths.size(); i++) 77 | { 78 | std::cout << paths[i] << ' '; 79 | } 80 | } -------------------------------------------------------------------------------- /sem13/left-look-same-as-right-look.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct Node 7 | { 8 | int data; 9 | Node* left; 10 | Node* right; 11 | 12 | Node(int data) 13 | : data(data), left(nullptr), right(nullptr) {} 14 | }; 15 | 16 | bool leftLookSameAsRightLook(Node* t) 17 | { 18 | if (t == nullptr) 19 | { 20 | return false; 21 | } 22 | 23 | std::queue level; 24 | level.push(t); 25 | bool isSimilar = true; 26 | 27 | while (!level.empty()) 28 | { 29 | int elements = level.size(); 30 | 31 | Node* leftmost = level.front(); 32 | Node* current = level.front(); 33 | 34 | for (int i = 0; i < elements; i++) 35 | { 36 | current = level.front(); 37 | level.pop(); 38 | 39 | if (current->left) 40 | { 41 | level.push(current->left); 42 | } 43 | 44 | if (current->right) 45 | { 46 | level.push(current->right); 47 | } 48 | } 49 | 50 | if (current->data != leftmost->data) 51 | { 52 | isSimilar = false; 53 | break; 54 | } 55 | } 56 | 57 | return isSimilar; 58 | } 59 | 60 | int main() 61 | { 62 | // 1 63 | // / \ 64 | // 2 2 65 | // / \ / \ 66 | // 3 4 9 11 67 | // / 68 | // 5 69 | 70 | Node* root = new Node(1); 71 | root->left = new Node(2); 72 | root->left->left = new Node(3); 73 | root->left->right = new Node(4); 74 | root->left->right->left = new Node(5); 75 | root->right = new Node(2); 76 | root->right->left = new Node(9); 77 | root->right->right = new Node(11); 78 | 79 | std::cout << leftLookSameAsRightLook(root) << std::endl; 80 | 81 | // 1 82 | // / \ 83 | // 2 2 84 | // / \ / \ 85 | // 3 4 9 3 86 | // / 87 | // 5 88 | 89 | Node* root1 = new Node(1); 90 | root1->left = new Node(2); 91 | root1->left->left = new Node(3); 92 | root1->left->right = new Node(4); 93 | root1->left->right->left = new Node(5); 94 | root1->right = new Node(2); 95 | root1->right->left = new Node(9); 96 | root1->right->right = new Node(3); 97 | 98 | std::cout << leftLookSameAsRightLook(root1); 99 | } -------------------------------------------------------------------------------- /sem13/left-right-look.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Node 6 | { 7 | int data; 8 | Node* left; 9 | Node* right; 10 | 11 | Node(int data) 12 | : data(data), left(nullptr), right(nullptr) {} 13 | }; 14 | 15 | void rightLook(Node* t, std::vector& view) 16 | { 17 | if (t == nullptr) 18 | { 19 | return; 20 | } 21 | 22 | std::queue level; 23 | level.push(t); 24 | 25 | while (!level.empty()) 26 | { 27 | int elements = level.size(); 28 | Node* current = level.front(); 29 | 30 | for (int i = 0; i < elements; i++) 31 | { 32 | current = level.front(); 33 | level.pop(); 34 | 35 | if (current->left) 36 | { 37 | level.push(current->left); 38 | } 39 | 40 | if (current->right) 41 | { 42 | level.push(current->right); 43 | } 44 | } 45 | 46 | view.push_back(current->data); 47 | } 48 | } 49 | 50 | void leftLook(Node* t, std::vector& view) 51 | { 52 | if (t == nullptr) 53 | { 54 | return; 55 | } 56 | 57 | std::queue level; 58 | level.push(t); 59 | 60 | while (!level.empty()) 61 | { 62 | int elements = level.size(); 63 | 64 | Node* current = level.front(); 65 | view.push_back(current->data); 66 | 67 | for (int i = 0; i < elements; i++) 68 | { 69 | current = level.front(); 70 | level.pop(); 71 | 72 | if (current->left) 73 | { 74 | level.push(current->left); 75 | } 76 | 77 | if (current->right) 78 | { 79 | level.push(current->right); 80 | } 81 | } 82 | } 83 | } 84 | 85 | int main() 86 | { 87 | // 1 88 | // / \ 89 | // 2 2 90 | // / \ / \ 91 | // 3 4 9 11 92 | // / 93 | // 5 94 | 95 | Node* root = new Node(1); 96 | root->left = new Node(2); 97 | root->left->left = new Node(3); 98 | root->left->right = new Node(4); 99 | root->left->right->left = new Node(5); 100 | root->right = new Node(2); 101 | root->right->left = new Node(9); 102 | root->right->right = new Node(11); 103 | 104 | std::vector viewRight; 105 | std::vector viewLeft; 106 | rightLook(root, viewRight); 107 | leftLook(root, viewLeft); 108 | 109 | std::cout << "Left: "; 110 | for (int i = 0; i < viewLeft.size(); i++) 111 | { 112 | std::cout << viewLeft[i] << ' '; 113 | } 114 | std::cout << '\n'; 115 | 116 | std::cout << "Right: "; 117 | for (int i = 0; i < viewRight.size(); i++) 118 | { 119 | std::cout << viewRight[i] << ' '; 120 | } 121 | } -------------------------------------------------------------------------------- /sem14/README.md: -------------------------------------------------------------------------------- 1 | # Упраженение № 14 2 | # Задачи върху дървета и графи (част 2) 3 | 4 | ### Задача 1. 5 | Дадено е двоично дърво, което е представено със следната структура:
6 | 7 | ```cpp 8 | template 9 | struct Node 10 | { 11 | T data; 12 | Node *left, *right; 13 | }; 14 | ``` 15 | 16 | Нека върховете на това дърво са символи.
17 | Да се напише функция, която по даден корен на дървото връща думите, които се образуват по нивата на дървото.
18 | 19 | ### Задача 2. 20 | Нека по даден корен към двоичното дърво се върне обхождането на това дърво по нива, но на зиг-заг, т.е. първото ниво от ляво на дясно, второто от дясно на ляво и т.н.
21 | Да се използва представянето от предната задача. 22 | 23 | ### Задача 3. 24 | Ниво L на двоично дърво съдържа всички възли на дървото, които се намират на разстояние L от корена на дървото.
25 | Да се напише функция, която приема като параметри указатели към корените на две двоични дървета от цели числа и проверява дали е вярно, че елементите на всяко ниво L на едното дърво съвпадат с елементите на съответното ниво L на другото дърво. 26 | 27 | ### Задача 4. 28 | Даден е ориентиран граф и стартов връх v. Да се напише функция, която намира сумата на елементите, които са на четно разстояние от v и сумата на елементите, които са на нечетно разстояние от v. 29 |
30 | Вход:
31 | цяло число n - брой на върховете
32 | цяло число m - брой на ребрата
33 | m реда във вида a b, което репрезентира ребро от а към b.
34 | 35 | Пример:
36 | 37 | Вход: 38 | 5 39 | 7 40 | 0 4 41 | 0 3 42 | 0 1 43 | 1 3 44 | 1 2 45 | 3 2 46 | 4 3 47 | 0 48 | 49 | Изход: 50 | 2, 8 51 | 52 | ### Задача 5. 53 | Даден е неориентиран граф. Да се намери сумата на средно аритметичното на върховете на всички свързани компоненти в графа. 54 |
55 | Вход:
56 | цяло число n - брой на върховете
57 | цяло число m - брой на ребрата
58 | m реда във вида a b, което означава ребро от а към b.
59 | 60 | Пример:
61 | 62 | Вход: 63 | 9 64 | 8 65 | 7 8 66 | 4 5 67 | 4 6 68 | 5 6 69 | 2 1 70 | 0 3 71 | 0 1 72 | 2 3 73 | 74 | Изход: 14 75 | 76 | ### Задача 6. 77 | По даден корен на двоично дърво, върнете всички пътища от корена до листо в произволен ред. 78 | 79 | -------------------------------------------------------------------------------- /sem14/graph problems/connected-components-average.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class Graph 6 | { 7 | int vertices; 8 | std::vector> adjList; 9 | 10 | public: 11 | Graph(int vertices) 12 | : vertices(vertices), adjList(vertices, std::vector()) {} 13 | 14 | Graph(int vertices, std::vector> edges) : Graph(vertices) 15 | { 16 | int edgesCount = edges.size(); 17 | for (int i = 0; i < edgesCount; i++) 18 | { 19 | adjList[edges[i].first].push_back(edges[i].second); 20 | adjList[edges[i].second].push_back(edges[i].first); 21 | } 22 | } 23 | 24 | double getAverageOfConnectedComponents(); 25 | void BFS(int start, std::vector& visited, int& sum, int& vertexCount); 26 | }; 27 | 28 | void Graph::BFS(int start, std::vector& visited, int& sum, int& vertexCount) 29 | { 30 | std::queue q; 31 | q.push(start); 32 | visited[start] = true; 33 | 34 | while (!q.empty()) 35 | { 36 | int currentVertex = q.front(); 37 | q.pop(); 38 | 39 | sum += currentVertex; 40 | vertexCount++; 41 | 42 | for (int i = 0; i < adjList[currentVertex].size(); i++) 43 | { 44 | if (!visited[adjList[currentVertex][i]]) 45 | { 46 | visited[adjList[currentVertex][i]] = true; 47 | q.push(adjList[currentVertex][i]); 48 | } 49 | } 50 | } 51 | 52 | } 53 | 54 | double Graph::getAverageOfConnectedComponents() 55 | { 56 | std::vector visited(adjList.size(), false); 57 | 58 | double result = 0; 59 | for (int i = 0; i < adjList.size(); i++) 60 | { 61 | if (!visited[i]) 62 | { 63 | int vertexCount = 0, sum = 0; 64 | BFS(i, visited, sum, vertexCount); 65 | result += ((double)sum / vertexCount); 66 | } 67 | } 68 | return result; 69 | } 70 | 71 | int main() 72 | { 73 | // (7) --- (8) => 7.5 74 | // 75 | // (4) ----- (5) => 5 76 | // \ / 77 | // \ / 78 | // \ (6) / 79 | // 80 | // _______________________ 81 | // | | => 1.5 82 | // (0) --- (1) --- (2) --- (3) 83 | 84 | int n = 9; 85 | int m = 8; 86 | std::vector> edges = 87 | {{7, 8}, 88 | {4, 5}, {4, 6}, {5, 6}, 89 | {2, 1}, {0, 3}, {0, 1}, {2, 3}}; 90 | 91 | Graph g(n, edges); 92 | std::cout << g.getAverageOfConnectedComponents(); 93 | } -------------------------------------------------------------------------------- /sem14/graph problems/sums-of-even-odd-distances.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class Graph 6 | { 7 | int vertices; 8 | std::vector> adjList; 9 | 10 | public: 11 | Graph(int vertices) : vertices(vertices), adjList(vertices, std::vector()) {} 12 | 13 | Graph(int vertices, std::vector> edges) : Graph(vertices) 14 | { 15 | int edgesCount = edges.size(); 16 | for (int i = 0; i < edgesCount; i++) 17 | { 18 | adjList[edges[i].first].push_back(edges[i].second); 19 | } 20 | } 21 | 22 | void getEvenSumOddSum(int start, int& odd, int& even) 23 | { 24 | std::queue> q; // true -> even 25 | std::vector visited(vertices, false); 26 | 27 | q.push({start, true}); 28 | visited[start] = true; 29 | 30 | while (!q.empty()) 31 | { 32 | std::pair current = q.front(); 33 | q.pop(); 34 | 35 | if (current.second) 36 | { 37 | even += current.first; 38 | } 39 | else 40 | { 41 | odd += current.first; 42 | } 43 | 44 | int size = adjList[current.first].size(); 45 | for (int i = 0; i < size; i++) 46 | { 47 | int neightbour = adjList[current.first][i]; 48 | if (!visited[neightbour]) 49 | { 50 | q.push(std::make_pair(neightbour, !current.second)); 51 | visited[neightbour] = true; 52 | } 53 | } 54 | } 55 | } 56 | }; 57 | 58 | int main() 59 | { 60 | // ┌------------┐ 61 | // | | 62 | // | (1) ←-- (0) ---→ (4) 63 | // | |\ / 64 | // | | \ / 65 | // | | └ → (2) / 66 | // | | ↑ / 67 | // | | / / 68 | // | | / / 69 | // | | / / 70 | // | | / / 71 | // | | / / 72 | // | | / / 73 | // | ↓/ / 74 | // └-→ (3) ←-┘ 75 | 76 | 77 | int n = 5; 78 | int m = 7; 79 | std::vector> edges = 80 | {{0, 4}, {0, 3}, {0, 1}, 81 | {1, 3}, {1, 2}, {3, 2}, {4, 3}}; 82 | 83 | Graph g(n, edges); 84 | int start = 0; 85 | int even = 0, odd = 0; 86 | g.getEvenSumOddSum(start, even, odd); 87 | std::cout << "Even: " << even << "\nOdd: " << odd; 88 | std::cout << '\n'; 89 | 90 | // (7) -> (8) 91 | // 92 | // (4) -------> (5) 93 | // \ / 94 | // \ / 95 | // \> (6) > edges1 = 104 | {{7, 8}, {4, 5}, {4, 6}, {5, 6}, 105 | {2, 1}, {0, 3}, {0, 1}, {2, 3}}; 106 | 107 | Graph g1(n1, edges1); 108 | int start1 = 0; 109 | int even1 = 0, odd1 = 0; 110 | g1.getEvenSumOddSum(start1, even1, odd1); 111 | std::cout << "Even: " << even1 << "\nOdd: " << odd1; 112 | } -------------------------------------------------------------------------------- /sem14/tree-problems.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | struct Node 7 | { 8 | T data; 9 | Node *left, *right; 10 | 11 | Node(T data) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | void levelOrderTraversal(Node* root, std::vector& levels) 15 | { 16 | if (!root) 17 | { 18 | return; 19 | } 20 | 21 | std::queue*> q; 22 | q.push(root); 23 | 24 | while (!q.empty()) 25 | { 26 | int size = q.size(); 27 | std::string level; 28 | 29 | for (int i = 0; i < size; i++) 30 | { 31 | Node* r = q.front(); 32 | q.pop(); 33 | 34 | level.push_back(r->data); 35 | 36 | if (r->left) 37 | { 38 | q.push(r->left); 39 | } 40 | if (r->right) 41 | { 42 | q.push(r->right); 43 | } 44 | } 45 | 46 | levels.push_back(level); 47 | } 48 | } 49 | 50 | template 51 | void levelZigZagOrder(Node* root, std::vector>& levels) 52 | { 53 | if (!root) 54 | { 55 | return; 56 | } 57 | 58 | std::queue*> q; 59 | q.push(root); 60 | int levelCheck = 0; 61 | 62 | while (!q.empty()) 63 | { 64 | int n = q.size(); 65 | std::vector level; 66 | 67 | while(n--) 68 | { 69 | Node* r = q.front(); 70 | q.pop(); 71 | 72 | if (levelCheck % 2) 73 | { 74 | level.insert(level.begin(), r->data); 75 | } 76 | else 77 | { 78 | level.push_back(r->data); 79 | } 80 | 81 | if (r->left) 82 | { 83 | q.push(r->left); 84 | } 85 | if (r->right) 86 | { 87 | q.push(r->right); 88 | } 89 | } 90 | levelCheck++; 91 | levels.push_back(level); 92 | } 93 | } 94 | 95 | struct Room 96 | { 97 | std::string name; 98 | 99 | Room(std::string name) : name(name) {} 100 | }; 101 | 102 | struct Door 103 | { 104 | bool isLocked; 105 | 106 | Door(bool isLocked = false) : isLocked(isLocked) {} 107 | 108 | bool isLockedCheck() 109 | { 110 | return isLocked; 111 | } 112 | }; 113 | 114 | class Maze 115 | { 116 | private: 117 | std::vector>>> maze; 118 | int vertices; 119 | 120 | int getRoomIndex(Room* city) 121 | { 122 | for (int i = 0; i < vertices; i++) 123 | { 124 | if (maze[i].first->name == city->name) 125 | { 126 | return i; 127 | } 128 | } 129 | return -1; 130 | } 131 | 132 | void getSuccessors(Room* start, std::vector>& succ) 133 | { 134 | int index = getRoomIndex(start); 135 | succ = maze[index].second; 136 | } 137 | 138 | void BFS(Room* start, std::vector& visited) 139 | { 140 | std::queue q; 141 | q.push(start); 142 | 143 | int startIndex = getRoomIndex(start); 144 | visited[startIndex] = true; 145 | 146 | while (!q.empty()) 147 | { 148 | Room* current = q.front(); 149 | q.pop(); 150 | 151 | std::vector> succ; 152 | getSuccessors(current, succ); 153 | 154 | for (int i = 0; i < succ.size(); i++) 155 | { 156 | if (succ[i].first->isLockedCheck()) 157 | { 158 | continue; 159 | } 160 | 161 | int ind = getRoomIndex(succ[i].second); 162 | visited[ind] = true; 163 | q.push(succ[i].second); 164 | } 165 | } 166 | } 167 | 168 | public: 169 | 170 | Maze(int vertices, std::vector>>> v) 171 | : vertices(vertices), maze(v) {} 172 | 173 | bool isReachable(Room* start, Room* end) 174 | { 175 | std::vector visited(vertices, false); 176 | int startIndex = getRoomIndex(start); 177 | int endIndex = getRoomIndex(end); 178 | 179 | BFS(start, visited); 180 | return visited[endIndex]; 181 | } 182 | }; 183 | 184 | template 185 | void free(Node* root) 186 | { 187 | if (!root) 188 | { 189 | return; 190 | } 191 | 192 | free(root->left); 193 | free(root->right); 194 | delete root; 195 | } 196 | 197 | int main() 198 | { 199 | Node* root = new Node('a'); 200 | root->left = new Node('b'); 201 | root->left->left = new Node('c'); 202 | root->left->right = new Node('d'); 203 | root->left->right->left = new Node('e'); 204 | root->right = new Node('f'); 205 | root->right->left = new Node('g'); 206 | root->right->right = new Node('h'); 207 | root->right->left->left = new Node('i'); 208 | root->right->right->left = new Node('j'); 209 | 210 | std::vector levels; 211 | levelOrderTraversal(root, levels); 212 | 213 | for (int i = 0; i < levels.size(); i++) 214 | { 215 | std::cout << levels[i] << ' '; 216 | } 217 | std::cout << '\n'; 218 | 219 | std::vector> levelsZigZag; 220 | levelZigZagOrder(root, levelsZigZag); 221 | 222 | for (int i = 0; i < levelsZigZag.size(); i++) 223 | { 224 | for (int j = 0; j < levelsZigZag[i].size(); j++) 225 | { 226 | std::cout << levelsZigZag[i][j] << ' '; 227 | } 228 | std::cout << '\n'; 229 | } 230 | 231 | free(root); 232 | 233 | Door* d1 = new Door(); 234 | Door* d2 = new Door(1); 235 | 236 | Room* r1 = new Room("r1"); 237 | Room* r2 = new Room("r2"); 238 | Room* r3 = new Room("r3"); 239 | Room* r4 = new Room("r4"); 240 | Room* r5 = new Room("r5"); 241 | Room* r6 = new Room("r6"); 242 | 243 | std::vector>>> v{ 244 | {r1, std::vector>{{d1, r2}, {d2, r3}}}, 245 | {r2, std::vector>{{d1, r3}, {d1, r4}}}, 246 | {r3, std::vector>{{d2, r5}, {d2, r4}}}, 247 | {r4, std::vector>{{d2, r6}}}, 248 | {r5, std::vector>{}}, 249 | {r6, std::vector>{}}}; 250 | 251 | Maze m(6, v); 252 | 253 | std::cout << m.isReachable(r1, r4) << '\n'; 254 | std::cout << m.isReachable(r1, r6) << '\n'; 255 | std::cout << m.isReachable(r4, r6) << '\n'; 256 | 257 | delete d1; 258 | delete d2; 259 | delete r1; 260 | delete r2; 261 | delete r3; 262 | delete r4; 263 | delete r5; 264 | delete r6; 265 | } 266 | -------------------------------------------------------------------------------- /sem15/README.md: -------------------------------------------------------------------------------- 1 | # Упраженение № 15 2 | # Хеш таблици. 3 | 4 | ## STL complexity analysis 5 | 6 | ## Въведение в структура от данни - хеш таблица 7 | Хеш таблица е структура от данни, която асоциира ключ с дадена стойност. Това се случва чрез така наречената хешираща функция (hash function).
8 | 9 | ![Hash table](../media/sem15/sem15-hash-basic.png) 10 | 11 | **Всяка имплементация на хеш таблица съдържа следните 3 компонента:** 12 | - хешираща функция 13 | - структура от данни, която поддържа основните операции: insert, search, delete 14 | - структура от данни, която да се справя с колизии 15 | 16 | ### Хешираща функция 17 | Функция, която по даден ключ генерира индекс - мястото за стойността, съответстваща на ключа.
18 | 19 | Проблем: Ако два различни ключа получат един и същи индекс, това се нарича колизия. Тогава трябва да бъде използвана допълнителна структура от данни за справянето с тези колизии.
20 | 21 | **Кога една хешираща функция е добра?** 22 | - бърза 23 | - предизвиква малко колизии 24 | - броят на колизиите на различните индекси е сравнително равен 25 | 26 | **Добри хеш функции?** 27 | k - ключ 28 | m - размер на хеш таблицата 29 | 30 | 1. Division method 31 | 32 | h(k) = k mod n 33 | 34 | 2. Multiplication method 35 | 36 | h(k) = floor(m (kA mod 1)) 37 | 38 | 3. Universal hashing 39 | Хеширащата функция се избира независимо от ключа. 40 | 41 | **Как се справяме с колизии?** 42 | 1. Chaining 43 | При генреиране на един и същи индекс, стойностите се съхраняват в двойно-свързан списък на този индекс. 44 | 45 | ![Chaining](../media/sem15/sem15-chaining.png) 46 | 47 | 2. Linear probing 48 | Проверява се дали следващия слот е свободен. 49 | 50 | 3. Quadratic probing 51 | Като линейното пробване, но разстоянието между слотовете се увеличава с повече от 1. 52 | 53 | 4. Double hashing 54 | При изникване на колизия се смята нова хеш функция за намиране на нов слот. 55 | 56 | ## Задачи 57 | 58 | ### Задача 1. 59 | Да се напише функция, която намира левия изглед на дърво. 60 | 61 | ### Задача 2. 62 | Да се открие дали вектор от цели числа съдържа подвектор, сумата на елементите, на който е 0. 63 | 64 | 69 | 70 | Решение чрез unordered_set:
71 | Ако имаме добра хешираща функция с константна сложност, то:
72 | Създаваме множество от сумите, до индекс i. Ако:
73 | 74 | // 4 1 2 -3 6 5 75 | // 4 5 7 4 76 | 77 | - Дадена сума съществува, то сме намерили подвектор със сума 0. 78 | - Ако намерим сума 0. 79 | 80 | Сложност по време: O(n)
81 | Сложност по памет: O(n)
82 | 83 | ### Задача 3. 84 | Да се намерят всички двойки числа в масив, които имат абсолютна разлика k.
85 | 86 | Наивно решение: разглеждаме всяка двойка в масива и я връщаме, ако сме намерили желаната разлика.
87 | Сложност по време: O(n^2) 88 | 89 | std::vector v = {1, 5, 2, 4}; 90 | int k = 3; 91 | (1 , 4) 92 | (5 , 2) 93 | 94 | ### Задача 4. 95 | Сортиране по честота и индекс
96 | Да се сортират елементите на масив спрямо тяхната честота и индекс.
97 | Ако два елемента имат различна честота, то този с по-голяма честота е първи.
98 | Ако два елемента имат еднаква честота, то този с по-малък индекс е първи.
99 | 100 | ### Задача 5. 101 | Даден е вектор от нули и единици. Да се намери най-големия подвектор, който има равен брой нули и единици. 102 | -------------------------------------------------------------------------------- /sem15/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct Node { 7 | int data; 8 | Node* left; 9 | Node* right; 10 | 11 | Node(int data) 12 | : data(data), left(nullptr), right(nullptr) {} 13 | }; 14 | 15 | void leftLook(Node* root, int level, std::unordered_map& m) 16 | { 17 | if (root == nullptr) 18 | { 19 | return; 20 | } 21 | 22 | if (m.find(level) == m.end()) 23 | { 24 | m[level] = root->data; 25 | } 26 | 27 | leftLook(root->left, level + 1, m); 28 | leftLook(root->right, level + 1, m); 29 | } 30 | 31 | void leftLook(Node* root) 32 | { 33 | std::unordered_map m; 34 | leftLook(root, 0, m); 35 | int size = m.size(); 36 | for (int i = 0; i < size; i++) 37 | { 38 | std::cout << m.at(i) << " "; 39 | } 40 | std::cout << std::endl; 41 | } 42 | 43 | bool existsZeroSumSub(const std::vector& numbers) 44 | { 45 | std::unordered_set sums; 46 | int sum = 0; 47 | 48 | int size = numbers.size(); 49 | for (int i = 0; i < size; i++) 50 | { 51 | sum += numbers[i]; 52 | 53 | // - Дадена сума съществува, то сме намерили подвектор със сума 0. 54 | // - Ако намерим сума 0. 55 | if (sum == 0 || sums.find(sum) != sums.end()) 56 | { 57 | return true; 58 | } 59 | 60 | sums.insert(sum); 61 | } 62 | 63 | return false; 64 | } 65 | 66 | void printPairs(const std::vector& v, int k) 67 | { 68 | int size = v.size(); 69 | std::unordered_set elements; 70 | 71 | for (int i = 0; i < size; i++) 72 | { 73 | if (elements.find(v[i] - k) != elements.end()) 74 | { 75 | std::cout << v[i] - k << " , " << v[i] << std::endl; 76 | } 77 | 78 | if (elements.find(v[i] + k) != elements.end()) 79 | { 80 | std::cout << v[i] + k << " , " << v[i] << std::endl; 81 | } 82 | 83 | elements.insert(v[i]); 84 | } 85 | } 86 | 87 | bool comparator(const std::tuple& lhs, const std::tuple& rhs) 88 | { 89 | if (std::get<1>(lhs) == std::get<1>(rhs)) 90 | { 91 | return std::get<2>(lhs) < std::get<2>(rhs); 92 | } 93 | 94 | return std::get<1>(lhs) > std::get<1>(rhs); 95 | } 96 | 97 | void sortByFeqAndIndex(std::vector& v) 98 | { 99 | int size = v.size(); 100 | if (size < 2) 101 | { 102 | return; 103 | } 104 | 105 | std::unordered_map> histogram; 106 | 107 | for (int i = 0; i < size; i++) 108 | { 109 | if (histogram.find(v[i]) != histogram.end()) 110 | { 111 | histogram[v[i]].first++; 112 | } 113 | else 114 | { 115 | histogram[v[i]] = {1, i}; 116 | } 117 | } 118 | 119 | std::vector> result; 120 | for (auto it : histogram) 121 | { 122 | result.push_back(std::make_tuple(it.first, it.second.first, it.second.second)); 123 | } 124 | 125 | std::sort(result.begin(), result.end(), comparator); 126 | 127 | for (int i = 0; i < size; i++) 128 | { 129 | v[i] = std::get<0>(result[i]); 130 | } 131 | } 132 | 133 | int main() 134 | { 135 | Node* root = new Node(1); 136 | root->left = new Node(2); 137 | root->right = new Node(3); 138 | root->left->left = new Node(4); 139 | root->right->left = new Node(5); 140 | root->right->right = new Node(6); 141 | root->right->left->left = new Node(7); 142 | 143 | leftLook(root); 144 | 145 | std::vector zeroSumFalse = {1, 2, 3, 4}; 146 | std::cout << std::boolalpha << existsZeroSumSub(zeroSumFalse) << std::endl; 147 | 148 | std::vector zeroSumTrue = {5, 1, 2, -3, 4}; 149 | std::cout << std::boolalpha << existsZeroSumSub(zeroSumTrue) << std::endl; 150 | 151 | std::vector v = {1, 5, 2, 4}; 152 | int k = 3; 153 | printPairs(v, k); 154 | 155 | } 156 | --------------------------------------------------------------------------------