├── AVL_DoS_Tree-vs-Tiered_Vector ├── AVL_Tree │ └── AVL.hpp ├── Tiered_Vector │ ├── Deque.hpp │ ├── FixedDeque.hpp │ └── TieredVector.hpp └── Time_Tests │ ├── AVL-Tiered_Vector - eraseAVL.pdf │ ├── AVL-Tiered_Vector - eraseTV.pdf │ ├── AVL-Tiered_Vector - indexAVL.pdf │ ├── AVL-Tiered_Vector - indexTV.pdf │ ├── AVL-Tiered_Vector - insertAVL.pdf │ ├── AVL-Tiered_Vector - insertTV.pdf │ ├── AVL-Tiered_Vector - popFrontAVL.pdf │ ├── AVL-Tiered_Vector - popFrontTV.pdf │ ├── AVL-Tiered_Vector - pushBackAVL.pdf │ ├── AVL-Tiered_Vector - pushBackTV.pdf │ ├── README.md │ └── Time_Tests │ └── TimeTests.cpp ├── Advanced-Intel_intrinsics _ BS_optimization ├── Intel_intrinsics-Binary_search.cpp ├── README.md └── Tests.png ├── Advanced-Memory_Allocator └── МemoryАllocator.cpp ├── Advanced-Timer_Scheduler ├── InvokableTask.hpp ├── SchedulableTask.hpp ├── Source.cpp ├── TaskPool.hpp └── TimerScheduler.hpp ├── AdvancedDS-AVL_DoS_Tree ├── AVL.hpp ├── Source.cpp └── Tests.hpp ├── AdvancedDS-Binomial_Heap ├── BinomialHeap.hpp ├── BinomialTree.hpp └── Source.cpp ├── AdvancedDS-DSW_algorithm └── DSW.cpp ├── AdvancedDS-Deque └── Deque.hpp ├── AdvancedDS-Tiered_Vector ├── Deque.hpp ├── FixedDeque.hpp ├── Source.cpp ├── Tests.hpp └── TieredVector.hpp ├── README.md ├── Sem_01 ├── Complexity.cpp ├── README.md ├── Sorting algorithms │ ├── BubbleSort.cpp │ ├── InsertionSort.cpp │ ├── README.md │ └── SelectionSort.cpp └── images │ └── AsymptoticNotations.png ├── Sem_02 ├── README.md ├── Sorting algorithms (Pt. 2) │ ├── CountingSort.cpp │ ├── MergeSort.cpp │ ├── QuickSort.cpp │ ├── README.md │ └── images │ │ ├── MergeSort.png │ │ ├── MergeSortComplexity.png │ │ └── QuickSort.png └── images │ ├── Task02.png │ └── Task03.png ├── Sem_03 ├── GenerateBoolVectors.cpp ├── README.md └── Vector │ ├── iterator.hpp │ └── vector.hpp ├── Sem_04 ├── DoublyLinkedList │ ├── list.hpp │ └── main.cpp ├── MergeSort_SLL.cpp ├── QuickSort_SLL.cpp ├── README.md ├── SinglyLinkedList │ ├── forward_list.hpp │ └── main.cpp ├── Solutions │ ├── Task03.cpp │ ├── Task04.cpp │ └── Task05.cpp └── images │ ├── Multilevel-list.png │ ├── One-level-list.png │ └── SLL.png ├── Sem_05 ├── Deque │ ├── deque.hpp │ └── main.cpp ├── Queue │ ├── CircularQueue.hpp │ ├── LinkedQueue.hpp │ └── QueueWithTemplateContainer.hpp ├── README.md ├── Solutions │ ├── Task01.cpp │ ├── Task02.cpp │ ├── Task03.cpp │ └── Task04.cpp └── Stack │ ├── ArrayStack.hpp │ ├── CheckValidBracketsString.cpp │ ├── LinkedStack.hpp │ └── StackWithTemplateContainer.hpp ├── Sem_06 ├── ParentArrayTreeHeight.cpp ├── README.md ├── Task01.cpp ├── Task02.cpp ├── Task03.cpp ├── Task04.cpp ├── Task05.cpp ├── Task06.cpp ├── Task07.cpp ├── Task08.cpp ├── Task09.cpp ├── Task10.cpp ├── Task11.cpp ├── Task12.cpp ├── TripleTree │ └── TripleTree.txt └── images │ ├── tree.png │ └── tripleTree.png ├── Sem_07 ├── Binary search tree │ ├── BST.hpp │ └── Source.cpp ├── README.md ├── Task01.cpp ├── Task02.cpp ├── Task03.cpp ├── Task04.cpp ├── Task05.cpp ├── Task06.cpp └── images │ ├── bst.png │ ├── catalan.png │ ├── insertInBst.png │ ├── removeInBst1.png │ ├── removeInBst2.png │ └── removeInBst3.png ├── Sem_08 ├── AVL tree │ └── AVL.md ├── README.md └── images │ ├── avl.png │ ├── detailedRotation.png │ ├── leftLeftCase.png │ ├── leftRightCase.png │ ├── rightLeftCase.png │ ├── rightRightCase.png │ └── rotation.png ├── Sem_09 ├── Priority queue │ ├── Source.cpp │ └── priority_queue.hpp ├── README.md ├── heapSort.cpp └── images │ ├── BinaryHeap.png │ ├── CompleteBinaryTree.png │ ├── heapify-1.png │ ├── heapify-2.png │ ├── heapify-3.png │ └── heapify.png ├── Sem_10 ├── README.md ├── images │ ├── ClosedHashing.png │ ├── Hash.jpeg │ └── OpenHashing.png └── subarraySumK.cpp └── Sem_11 ├── Graph algorithms ├── UnweightedGraph │ ├── GraphAlgorithms.cpp │ ├── GraphAlgorithms.h │ └── main.cpp └── WeightedGraph │ ├── GraphAlgorithms.cpp │ ├── GraphAlgorithms.h │ ├── UnionFind │ ├── UnionFind.cpp │ └── UnionFind.h │ └── main.cpp └── README.md /AVL_DoS_Tree-vs-Tiered_Vector/Tiered_Vector/Deque.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | template 6 | class Deque 7 | { 8 | private: 9 | T* data; 10 | 11 | size_t elementsCount; 12 | size_t capacity; 13 | 14 | size_t head; // index of the first element 15 | size_t tail; // index of the last element + 1 16 | 17 | void copyFrom(const Deque&); 18 | void moveFrom(Deque&&); 19 | 20 | void movePosition(size_t&, bool) const; 21 | 22 | public: 23 | Deque() = default; 24 | Deque(const Deque&); 25 | Deque(Deque&&); 26 | Deque& operator=(const Deque&); 27 | Deque& operator=(Deque&&); 28 | ~Deque(); 29 | 30 | void free(); 31 | 32 | void push_back(const T&); 33 | void push_back(T&&); 34 | void push_front(const T&); 35 | void push_front(T&&); 36 | 37 | void pop_back(); 38 | void pop_front(); 39 | 40 | const T& back() const; 41 | const T& front() const; 42 | T& back(); 43 | T& front(); 44 | 45 | const T& operator[](size_t) const; 46 | T& operator[](size_t); 47 | 48 | bool empty() const; 49 | size_t size() const; 50 | 51 | void setCapacity(size_t); 52 | size_t getCapacity() const; 53 | }; 54 | 55 | template 56 | void Deque::push_back(const T& el) 57 | { 58 | data[tail] = el; 59 | movePosition(tail, true); 60 | elementsCount++; 61 | } 62 | template 63 | void Deque::push_back(T&& el) 64 | { 65 | data[tail] = std::move(el); 66 | movePosition(tail, true); 67 | elementsCount++; 68 | } 69 | template 70 | void Deque::push_front(const T& el) 71 | { 72 | movePosition(head, false); 73 | data[head] = el; 74 | elementsCount++; 75 | } 76 | template 77 | void Deque::push_front(T&& el) 78 | { 79 | movePosition(head, false); 80 | data[head] = std::move(el); 81 | elementsCount++; 82 | } 83 | 84 | template 85 | void Deque::pop_back() 86 | { 87 | if (empty()) 88 | throw std::length_error("Empty deque!"); 89 | 90 | movePosition(tail, false); 91 | elementsCount--; 92 | } 93 | template 94 | void Deque::pop_front() 95 | { 96 | if (empty()) 97 | throw std::length_error("Empty deque!"); 98 | 99 | movePosition(head, true); 100 | elementsCount--; 101 | } 102 | 103 | template 104 | const T& Deque::back() const 105 | { 106 | if (empty()) 107 | throw std::length_error("Empty deque!"); 108 | 109 | size_t lastElement = tail; movePosition(lastElement, false); 110 | return data[lastElement]; 111 | } 112 | template 113 | const T& Deque::front() const 114 | { 115 | if (empty()) 116 | throw std::length_error("Empty deque!"); 117 | 118 | return data[head]; 119 | } 120 | template 121 | T& Deque::back() 122 | { 123 | if (empty()) 124 | throw std::length_error("Empty deque!"); 125 | 126 | size_t lastElement = tail; movePosition(lastElement, false); 127 | return data[lastElement]; 128 | } 129 | template 130 | T& Deque::front() 131 | { 132 | if (empty()) 133 | throw std::length_error("Empty deque!"); 134 | 135 | return data[head]; 136 | } 137 | 138 | template 139 | const T& Deque::operator[](size_t index) const 140 | { 141 | assert(index < elementsCount); 142 | return data[(head + index) % capacity]; 143 | } 144 | template 145 | T& Deque::operator[](size_t index) 146 | { 147 | assert(index < elementsCount); 148 | return data[(head + index) % capacity]; 149 | } 150 | 151 | template 152 | bool Deque::empty() const 153 | { 154 | return elementsCount == 0; 155 | } 156 | template 157 | size_t Deque::size() const 158 | { 159 | return elementsCount; 160 | } 161 | 162 | template 163 | void Deque::setCapacity(size_t capacity) 164 | { 165 | delete[] data; 166 | 167 | elementsCount = 0; 168 | this->capacity = capacity; 169 | data = new T[capacity]; 170 | 171 | head = tail = 0; 172 | } 173 | template 174 | size_t Deque::getCapacity() const 175 | { 176 | return capacity; 177 | } 178 | 179 | template 180 | void Deque::movePosition(size_t& currPosition, bool moveForward) const 181 | { 182 | if (moveForward) 183 | { 184 | (++currPosition) %= capacity; 185 | } 186 | else 187 | { 188 | if (currPosition == 0) 189 | currPosition = capacity - 1; 190 | else 191 | currPosition--; 192 | } 193 | } 194 | 195 | template 196 | Deque::Deque(const Deque& other) 197 | { 198 | copyFrom(other); 199 | } 200 | template 201 | Deque::Deque(Deque&& other) 202 | { 203 | moveFrom(std::move(other)); 204 | } 205 | template 206 | Deque& Deque::operator=(const Deque& other) 207 | { 208 | if (this != &other) 209 | { 210 | free(); 211 | copyFrom(other); 212 | } 213 | return *this; 214 | } 215 | template 216 | Deque& Deque::operator=(Deque&& other) 217 | { 218 | if (this != &other) 219 | { 220 | free(); 221 | moveFrom(std::move(other)); 222 | } 223 | return *this; 224 | } 225 | template 226 | Deque::~Deque() 227 | { 228 | free(); 229 | } 230 | 231 | template 232 | void Deque::free() 233 | { 234 | delete[] data; 235 | data = nullptr; 236 | } 237 | template 238 | void Deque::copyFrom(const Deque& other) 239 | { 240 | elementsCount = other.elementsCount; 241 | capacity = other.capacity; 242 | 243 | head = other.head; 244 | tail = other.tail; 245 | 246 | data = new T[capacity]; 247 | for (size_t i = 0; i < capacity; i++) 248 | data[i] = other.data[i]; 249 | } 250 | template 251 | void Deque::moveFrom(Deque&& other) 252 | { 253 | elementsCount = other.elementsCount; 254 | capacity = other.capacity; 255 | 256 | head = other.head; 257 | tail = other.tail; 258 | 259 | other.elementsCount = other.capacity = other.head = other.tail = 0; 260 | 261 | data = other.data; 262 | other.data = nullptr; 263 | } 264 | -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - eraseAVL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - eraseAVL.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - eraseTV.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - eraseTV.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - indexAVL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - indexAVL.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - indexTV.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - indexTV.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - insertAVL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - insertAVL.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - insertTV.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - insertTV.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - popFrontAVL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - popFrontAVL.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - popFrontTV.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - popFrontTV.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - pushBackAVL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - pushBackAVL.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - pushBackTV.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/AVL-Tiered_Vector - pushBackTV.pdf -------------------------------------------------------------------------------- /AVL_DoS_Tree-vs-Tiered_Vector/Time_Tests/README.md: -------------------------------------------------------------------------------- 1 | [Empirical results for the complexity of the two structures](https://docs.google.com/spreadsheets/d/1EjDW1ULRBT1YApFQg_rUwcLygcBGFCJxh4jqPk_5AvM/edit?usp=sharing) 2 | -------------------------------------------------------------------------------- /Advanced-Intel_intrinsics _ BS_optimization/Intel_intrinsics-Binary_search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | * @param hayStack - the input data that will be searched in 6 | * @param needles - the values that will be searched 7 | * @param indices - the indices of the needles (or -1 if the needle is not found) 8 | */ 9 | static void AVXBinarySearch(const int* hayStack, std::size_t hayStackCount, const int* needles, std::size_t needlesCount, int* indices) 10 | { 11 | const int needlesPerIteration = 8; 12 | 13 | __m256i onesVector = _mm256_set1_epi32(1); 14 | __m256i minusOnesVector = _mm256_set1_epi32(-1); 15 | 16 | for (size_t i = 0; i < needlesCount; i += needlesPerIteration) 17 | { 18 | __m256i indexVector = minusOnesVector; 19 | __m256i needleVector = _mm256_loadu_si256(reinterpret_cast(needles + i)); 20 | 21 | __m256i leftVector = _mm256_setzero_si256(); 22 | __m256i rightVector = _mm256_set1_epi32(hayStackCount - 1); 23 | if (hayStack[0] == hayStack[hayStackCount - 1]) 24 | rightVector = leftVector; 25 | 26 | __m256i leftGtRightVector = _mm256_setzero_si256(); 27 | 28 | while (_mm256_movemask_epi8(leftGtRightVector) != -1) 29 | { 30 | leftGtRightVector = _mm256_cmpgt_epi32(leftVector, rightVector); 31 | __m256i leftNotGtRightVector = _mm256_andnot_si256(leftGtRightVector, minusOnesVector); 32 | 33 | // mid = left + (right - left) / 2 34 | __m256i midPositionVector = _mm256_blendv_epi8(onesVector, 35 | _mm256_add_epi32(leftVector, _mm256_srli_epi32(_mm256_sub_epi32(rightVector, leftVector), 1)), leftNotGtRightVector); 36 | __m256i midElementVector = _mm256_blendv_epi8(onesVector, 37 | _mm256_i32gather_epi32(hayStack, midPositionVector, sizeof(int)), leftNotGtRightVector); 38 | 39 | __m256i equalVector = _mm256_blendv_epi8(onesVector, _mm256_cmpeq_epi32(needleVector, midElementVector), leftNotGtRightVector); 40 | __m256i greaterVector = _mm256_blendv_epi8(onesVector, _mm256_cmpgt_epi32(needleVector, midElementVector), leftNotGtRightVector); 41 | __m256i smallerOrEqualVector = _mm256_andnot_si256(greaterVector, minusOnesVector); 42 | 43 | indexVector = _mm256_blendv_epi8(indexVector, midPositionVector, equalVector); 44 | leftVector = _mm256_blendv_epi8(leftVector, _mm256_add_epi32(midPositionVector, onesVector), 45 | _mm256_and_si256(greaterVector, leftNotGtRightVector)); 46 | rightVector = _mm256_blendv_epi8(rightVector, _mm256_sub_epi32(midPositionVector, onesVector), 47 | _mm256_and_si256(smallerOrEqualVector, leftNotGtRightVector)); 48 | } 49 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(indices + i), indexVector); 50 | } 51 | } 52 | 53 | int main() 54 | { 55 | 56 | } -------------------------------------------------------------------------------- /Advanced-Intel_intrinsics _ BS_optimization/README.md: -------------------------------------------------------------------------------- 1 | # Intrinsics 2 | 3 | Intrinsics are a set of assembly-coded functions that allow developers to use C++ function calls and variables in place of assembly instructions. They are designed to enhance performance by providing inline expansions of the code, which eliminates the overhead of a function call. Additionally, intrinsics improve the readability of the code, assist with instruction scheduling, and reduce debugging time. 4 | 5 | [Here you can see a full list of **Intel intrinsics**](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html). 6 | 7 | One example of intrinsics (the one I've used for my solution) is the [**Advanced Vector Extensions (AVX)**](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions), which is an instruction set extension for x86 processors. AVX provides 256-bit floating-point operations and enables parallel processing of multiple data elements, which significantly enhances the performance of vectorized algorithms. 8 | 9 | To evaluate the performance of AVX, a series of tests were conducted to compare the speed of an ordinary Binary Search with that of an AVX Binary Search. The tests used various datasets and were repeated multiple times to ensure accuracy. The results showed a significant improvement in performance for the AVX Binary Search across all tests, indicating the efficacy of using intrinsics for optimization purposes. 10 | 11 | ![alt_text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Advanced-Intel_intrinsics%20_%20BS_optimization/Tests.png) 12 | -------------------------------------------------------------------------------- /Advanced-Intel_intrinsics _ BS_optimization/Tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Advanced-Intel_intrinsics _ BS_optimization/Tests.png -------------------------------------------------------------------------------- /Advanced-Timer_Scheduler/InvokableTask.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // A wrapper of a callable object (supporting operator()) 6 | class InvokableTask 7 | { 8 | public: 9 | template 10 | explicit InvokableTask(FunctionType&& f) 11 | : m_invokableTaskPtr{ std::make_unique>(std::move(f)) } 12 | {} 13 | 14 | InvokableTask(const InvokableTask&) = delete; 15 | InvokableTask& operator=(const InvokableTask&) = delete; 16 | 17 | InvokableTask(InvokableTask&& other) noexcept 18 | : m_invokableTaskPtr{ std::move(other.m_invokableTaskPtr) } 19 | {} 20 | 21 | void operator()() 22 | { 23 | m_invokableTaskPtr->invoke(); 24 | } 25 | void invoke() 26 | { 27 | m_invokableTaskPtr->invoke(); 28 | } 29 | 30 | private: 31 | struct Base 32 | { 33 | virtual void invoke() = 0; 34 | virtual ~Base() = default; 35 | }; 36 | 37 | template 38 | struct Task : Base 39 | { 40 | explicit Task(FunctionType&& func) : m_func{ std::move(func) } 41 | { 42 | static_assert(std::is_invocable_v); 43 | } 44 | void invoke() override 45 | { 46 | m_func(); 47 | } 48 | 49 | FunctionType m_func; 50 | }; 51 | 52 | std::unique_ptr m_invokableTaskPtr; 53 | }; -------------------------------------------------------------------------------- /Advanced-Timer_Scheduler/SchedulableTask.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InvokableTask.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class SchedulableTask 10 | { 11 | public: 12 | template 13 | explicit SchedulableTask(FunctionType&& f) 14 | : m_task{ std::make_shared(std::forward(f)) } 15 | {} 16 | 17 | template 18 | explicit SchedulableTask(FunctionType&& f, size_t hash) 19 | : m_task{ std::make_shared(std::forward(f)) } 20 | , m_hash{ hash } 21 | {} 22 | 23 | template 24 | explicit SchedulableTask(FunctionType&& f, std::chrono::steady_clock::duration interval, size_t maxReps) 25 | : m_task{ std::make_shared(std::forward(f)) } 26 | , m_interval{ interval } 27 | { 28 | if (maxReps > 0) 29 | { 30 | m_maxReps = maxReps; 31 | } 32 | } 33 | 34 | template 35 | explicit SchedulableTask(FunctionType&& f, size_t hash, std::chrono::steady_clock::duration interval, size_t maxReps) 36 | : m_task{ std::make_shared(std::forward(f)) } 37 | , m_hash{ hash } 38 | , m_interval{ interval } 39 | { 40 | if (maxReps > 0) 41 | { 42 | m_maxReps = maxReps; 43 | } 44 | } 45 | 46 | SchedulableTask(const SchedulableTask&) = delete; 47 | SchedulableTask& operator=(const SchedulableTask&) = delete; 48 | 49 | SchedulableTask(SchedulableTask&& other) noexcept 50 | : m_task{ std::move(other.m_task) } 51 | , m_isEnabled{ std::move(other.m_isEnabled) } 52 | , m_interval{ std::move(other.m_interval) } 53 | , m_maxReps{ std::move(other.m_maxReps) } 54 | , m_hash{ std::move(other.m_hash) } 55 | { 56 | } 57 | 58 | std::shared_ptr clone() 59 | { 60 | return std::shared_ptr(new SchedulableTask(this)); 61 | } 62 | 63 | void invoke() 64 | { 65 | setEnabled(false); 66 | m_task->invoke(); 67 | setEnabled(true); 68 | } 69 | 70 | void setEnabled(bool isEnabled) 71 | { 72 | *m_isEnabled = isEnabled; 73 | } 74 | bool isEnabled() const 75 | { 76 | return *m_isEnabled; 77 | } 78 | 79 | void setInterval(std::chrono::steady_clock::duration interval) 80 | { 81 | m_interval = interval; 82 | } 83 | std::optional interval() const 84 | { 85 | return m_interval; 86 | } 87 | 88 | void decrementMaxReps() 89 | { 90 | if (m_maxReps == 0) 91 | { 92 | return; 93 | } 94 | m_maxReps = m_maxReps.value() - 1; 95 | } 96 | std::optional maxReps() const 97 | { 98 | return m_maxReps; 99 | } 100 | 101 | std::optional hash() const 102 | { 103 | return m_hash; 104 | } 105 | 106 | private: 107 | std::shared_ptr m_task; 108 | std::shared_ptr m_isEnabled = std::make_shared(true); 109 | std::optional m_interval; 110 | std::optional m_maxReps; 111 | std::optional m_hash; 112 | 113 | SchedulableTask(SchedulableTask* st) 114 | : m_task{ st->m_task } 115 | , m_isEnabled{ st->m_isEnabled } 116 | , m_interval{ st->m_interval } 117 | , m_maxReps{ st->m_maxReps } 118 | , m_hash{ st->m_hash } 119 | {} 120 | }; -------------------------------------------------------------------------------- /Advanced-Timer_Scheduler/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "TimerScheduler.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using systemClock = std::chrono::system_clock; 7 | using steadyClock = std::chrono::steady_clock; 8 | using s_t = std::chrono::seconds; 9 | using ns_t = std::chrono::nanoseconds; 10 | 11 | // ST - single timer, RT - repeating timer 12 | // NB: The printing of the repeating timers might be messed because of the concurrent execution 13 | int main() 14 | { 15 | TimerScheduler s(2); 16 | s.start(); 17 | 18 | std::cout << "Starting single timers at " << systemClock::now() << std::endl; 19 | 20 | s.scheduleSingle("1", s_t(3), []() 21 | { 22 | std::cout << std::endl << "Executing first ST at " << systemClock::now() << std::endl; 23 | } 24 | ); 25 | s.scheduleSingle("2", s_t(4), []() 26 | { 27 | std::cout << std::endl << "Executing second ST at " << systemClock::now() << std::endl; 28 | std::this_thread::sleep_for(s_t(7)); 29 | } 30 | ); 31 | s.scheduleSingle("3", s_t(5), [](int a, double b) 32 | { 33 | std::cout << std::endl << "Executing third ST with params " << a << " and " << b << " at " << systemClock::now() << std::endl; 34 | }, 35 | 5, 23 36 | ); 37 | 38 | std::this_thread::sleep_for(s_t(15)); // wait the single timers 39 | 40 | ////////////////////////////////////////////////// 41 | 42 | std::cout << std::endl << "Starting repeating timers at " << systemClock::now() << std::endl; 43 | 44 | s.scheduleRepeat("4", TimerScheduler::IntervalReps{ s_t(2), 3 }, []() {std::cout << std::endl << "Executing first RT at " << systemClock::now() << std::endl; }); 45 | s.scheduleRepeat("5", TimerScheduler::IntervalReps{ s_t(2), 4 }, []() {std::cout << std::endl << "Executing second RT at " << systemClock::now() << std::endl; }); 46 | s.scheduleRepeat("6", TimerScheduler::IntervalReps{ s_t(2), 5 }, [](int a, int b) {std::cout << std::endl << "Executing third RT at " << systemClock::now() << std::endl; }, 2, 222); 47 | s.scheduleRepeat("7", TimerScheduler::IntervalReps{ s_t(2) }, []() {std::cout << std::endl << "Executing unbounded RT " << systemClock::now() << std::endl; }); 48 | 49 | std::this_thread::sleep_for(s_t(20)); // wait the repeating timers to be executed several times 50 | 51 | std::cout << std::endl << "Updating the interval of the unbounded repeating timer from 2 to 4 secs" << std::endl; 52 | s.updateInterval("7", s_t(4)); 53 | 54 | std::this_thread::sleep_for(s_t(15)); // wait the unbounded timer to be executed several times with its new interval 55 | 56 | std::cout << std::endl << "Cancelling the unbounded repeating timer" << std::endl; 57 | s.cancelTimer("7"); 58 | 59 | std::cout << std::endl << "Currently scheduled timers (0 is expected): " << s.currentlyScheduled() << std::endl; 60 | std::cout << std::endl << "Waiting some time to see that the canceled timer won't be executed again..." << std::endl; 61 | std::this_thread::sleep_for(s_t(10)); 62 | 63 | // We can also schedule a single timer and get its result when it's ready 64 | std::cout << std::endl << "Starting single timer" << std::endl; 65 | auto res = s.scheduleSingle("1", s_t(0), [](int param) { return sqrt(param); }, 121); 66 | std::cout << std::endl << "Getting its result... " << res.get() << std::endl; 67 | 68 | return 0; 69 | } -------------------------------------------------------------------------------- /Advanced-Timer_Scheduler/TaskPool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InvokableTask.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class TaskPool 16 | { 17 | public: 18 | explicit TaskPool(unsigned threadsCount = std::thread::hardware_concurrency()) 19 | { 20 | try 21 | { 22 | for (size_t i = 0; i < threadsCount; i++) 23 | { 24 | m_threads.emplace_back(&TaskPool::workerThread, this); 25 | } 26 | } 27 | catch (...) 28 | { 29 | m_isRunning = false; 30 | m_haveWorkEvent.notify_all(); 31 | throw; 32 | } 33 | } 34 | 35 | ~TaskPool() 36 | { 37 | m_isRunning = false; 38 | m_haveWorkEvent.notify_all(); 39 | for (auto&& t : m_threads) 40 | { 41 | t.join(); 42 | } 43 | } 44 | 45 | TaskPool(const TaskPool&) = delete; 46 | TaskPool& operator=(const TaskPool&) = delete; 47 | TaskPool(TaskPool&&) = delete; 48 | TaskPool& operator=(TaskPool&&) = delete; 49 | 50 | template 51 | void run(FunctionType&& f) 52 | { 53 | std::unique_lock lock(m_workMutex); 54 | m_tasksQueue.emplace(std::move(f)); 55 | lock.unlock(); 56 | 57 | m_haveWorkEvent.notify_one(); 58 | } 59 | 60 | private: 61 | std::atomic_bool m_isRunning = true; 62 | std::vector m_threads; 63 | std::queue m_tasksQueue; 64 | std::condition_variable m_haveWorkEvent; 65 | std::mutex m_workMutex; 66 | 67 | void workerThread() 68 | { 69 | while (m_isRunning) 70 | { 71 | std::unique_lock lock(m_workMutex); 72 | m_haveWorkEvent.wait(lock, [this] { return !m_tasksQueue.empty() || !m_isRunning; }); 73 | 74 | if (!m_isRunning) 75 | { 76 | return; 77 | } 78 | 79 | InvokableTask toExecute = std::move(m_tasksQueue.front()); 80 | 81 | m_tasksQueue.pop(); 82 | lock.unlock(); 83 | 84 | toExecute(); 85 | } 86 | } 87 | }; -------------------------------------------------------------------------------- /AdvancedDS-AVL_DoS_Tree/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "Tests.hpp" 2 | 3 | int main() 4 | { 5 | runTests(); 6 | } -------------------------------------------------------------------------------- /AdvancedDS-AVL_DoS_Tree/Tests.hpp: -------------------------------------------------------------------------------- 1 | #include "AVL.hpp" 2 | 3 | bool testInitialize() 4 | { 5 | AVL avl = { 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 8, 9, 10 }; 6 | if (avl.size() != 11) 7 | return false; 8 | 9 | for (size_t i = 0; i < avl.size(); i++) 10 | if (avl[i] != i) 11 | return false; 12 | return true; 13 | } 14 | 15 | bool testInsertWithOnlyLeftRotations() 16 | { 17 | AVL avl; 18 | 19 | for (size_t i = 0; i <= 200; i++) 20 | avl.insert(i); 21 | if (avl.size() != 201) 22 | return false; 23 | 24 | for (size_t i = 0; i < avl.size(); i++) 25 | if (avl[i] != i) 26 | return false; 27 | return true; 28 | } 29 | 30 | bool testInsertWithOnlyRightRotations() 31 | { 32 | AVL avl; 33 | 34 | for (size_t i = 200; i != 0; i--) 35 | avl.insert(i); 36 | if (avl.size() != 200) 37 | return false; 38 | 39 | for (size_t i = 0; i < avl.size(); i++) 40 | if (avl[i] != i + 1) 41 | return false; 42 | return true; 43 | } 44 | 45 | bool testInsertWithLeftRightRotationsAndRightLeftRotations() 46 | { 47 | AVL avl; 48 | 49 | for (size_t i = 0; i != 300; i += 3) 50 | { 51 | avl.insert(i); 52 | avl.insert(i + 2); 53 | avl.insert(i + 1); 54 | } 55 | for (size_t i = 300; i != 600; i += 3) 56 | { 57 | avl.insert(i + 2); 58 | avl.insert(i); 59 | avl.insert(i + 1); 60 | } 61 | if (avl.size() != 600) 62 | return false; 63 | 64 | for (size_t i = 0; i < avl.size(); i++) 65 | if (avl[i] != i) 66 | return false; 67 | return true; 68 | } 69 | 70 | bool testEraseOnlyLeavesAndNodesWithOneChild() 71 | { 72 | AVL avl; 73 | 74 | for (size_t i = 0; i < 300; i++) 75 | avl.insert(i); 76 | 77 | for (size_t i = 0; i < 50; i++) 78 | { 79 | avl.erase(i); 80 | avl.erase(299 - i); 81 | } 82 | 83 | if (avl.size() != 200) 84 | return false; 85 | 86 | for (size_t i = 0; i < avl.size(); i++) 87 | if (avl[i] != i + 50) 88 | return false; 89 | return true; 90 | } 91 | 92 | bool testEraseNodesWithTwoChildren() 93 | { 94 | AVL avl; 95 | 96 | for (size_t i = 0; i < 300; i++) 97 | avl.insert(i); 98 | 99 | for (size_t i = 0; i < 100; i++) 100 | avl.erase(i + 100); 101 | 102 | if (avl.size() != 200) 103 | return false; 104 | 105 | for (size_t i = 0; i < 100; i++) 106 | if (avl[i] != i) 107 | return false; 108 | for (size_t i = 100; i < 200; i++) 109 | if (avl[i] != i + 100) 110 | return false; 111 | return true; 112 | } 113 | 114 | bool testEraseRandom() 115 | { 116 | AVL avl; 117 | 118 | for (size_t i = 0; i < 300; i++) 119 | avl.insert(i); 120 | 121 | for (size_t i = 0; i < 50; i++) 122 | { 123 | avl.erase(i); 124 | avl.erase(100 + i); 125 | avl.erase(299 - i); 126 | } 127 | 128 | if (avl.size() != 150) 129 | return false; 130 | 131 | for (size_t i = 0; i < 50; i++) 132 | if (avl[i] != i + 50) 133 | return false; 134 | for (size_t i = 0; i < 50; i++) 135 | if (avl[i + 50] != i + 150) 136 | return false; 137 | for (size_t i = 0; i < 50; i++) 138 | if (avl[i + 100] != i + 200) 139 | return false; 140 | return true; 141 | } 142 | 143 | bool testCopy() 144 | { 145 | AVL avl; 146 | for (size_t i = 0; i <= 200; i++) 147 | avl.insert(i); 148 | if (avl.size() != 201) 149 | return false; 150 | 151 | AVL avl2(avl); 152 | if (avl.size() != avl2.size()) 153 | return false; 154 | for (size_t i = 0; i < avl2.size(); i++) 155 | if (avl[i] != avl2[i]) 156 | return false; 157 | 158 | AVL avl3 = { 1, 2, 3 }; 159 | avl3 = avl2; 160 | if (avl2.size() != avl3.size()) 161 | return false; 162 | for (size_t i = 0; i < avl3.size(); i++) 163 | if (avl2[i] != avl3[i]) 164 | return false; 165 | 166 | return true; 167 | } 168 | 169 | bool testRank() 170 | { 171 | AVL avl = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 172 | 173 | for (size_t i = 0; i < avl.size(); i++) 174 | if (avl.rank(i) != i) 175 | return false; 176 | if (avl.rank(15) != -1) 177 | return false; 178 | 179 | return true; 180 | } 181 | 182 | void runTests() 183 | { 184 | std::cout << "Test 1: " << testInitialize() << std::endl; 185 | std::cout << "Test 2: " << testInsertWithOnlyLeftRotations() << std::endl; 186 | std::cout << "Test 3: " << testInsertWithOnlyRightRotations() << std::endl; 187 | std::cout << "Test 4: " << testInsertWithLeftRightRotationsAndRightLeftRotations() << std::endl; 188 | std::cout << "Test 5: " << testEraseOnlyLeavesAndNodesWithOneChild() << std::endl; 189 | std::cout << "Test 6: " << testEraseNodesWithTwoChildren() << std::endl; 190 | std::cout << "Test 7: " << testEraseRandom() << std::endl; 191 | std::cout << "Test 8: " << testCopy() << std::endl; 192 | std::cout << "Test 9: " << testRank() << std::endl; 193 | } 194 | -------------------------------------------------------------------------------- /AdvancedDS-Binomial_Heap/BinomialHeap.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "BinomialTree.hpp" 4 | 5 | template 6 | class BinomialHeap; 7 | template 8 | void mergeHeaps(BinomialHeap& result, BinomialHeap& lhs, BinomialHeap& rhs); 9 | 10 | template 11 | class BinomialHeap 12 | { 13 | private: 14 | std::list> data; 15 | size_t size = 0; // number of currently contained values 16 | mutable std::optional min; 17 | 18 | void push_back(const BinomialTree& el); 19 | void pop_front(); 20 | 21 | size_t countr_zero(size_t n) const; 22 | size_t getRank() const; // get the rank of the first tree in the list 23 | 24 | void mergeWithCarry(BinomialTree& carry, size_t carryRank, BinomialHeap& result, BinomialHeap& lhs, BinomialHeap& rhs); 25 | 26 | public: 27 | BinomialHeap() = default; 28 | BinomialHeap(const T& value); 29 | BinomialHeap(T&& value); 30 | 31 | friend void mergeHeaps(BinomialHeap& result, BinomialHeap& lhs, BinomialHeap& rhs); // O(lgn) where n = max(lhs.size, rhs.size) 32 | 33 | // O(1) amortized 34 | void insert(const T& value); 35 | void insert(T&& value); 36 | 37 | const T& getMin() const; // O(1) 38 | // const T& extractMin(); // tbi 39 | }; 40 | 41 | template 42 | void BinomialHeap::mergeWithCarry(BinomialTree& carry, size_t carryRank, BinomialHeap& result, BinomialHeap& lhs, BinomialHeap& rhs) 43 | { 44 | while (!lhs.data.empty() && !rhs.data.empty()) 45 | { 46 | size_t lhsRank = lhs.getRank(); 47 | size_t rhsRank = rhs.getRank(); 48 | 49 | if (carryRank == lhsRank && carryRank == rhsRank) 50 | { 51 | result.push_back(carry); 52 | 53 | BinomialTree newCarry = mergeTrees(lhs.data.front(), rhs.data.front()); 54 | lhs.pop_front(); 55 | rhs.pop_front(); 56 | 57 | mergeWithCarry(newCarry, carryRank + 1, result, lhs, rhs); 58 | return; 59 | } 60 | else if (carryRank == lhsRank) 61 | { 62 | carry = mergeTrees(carry, lhs.data.front()); 63 | lhs.pop_front(); 64 | ++carryRank; 65 | } 66 | else if (carryRank == rhsRank) 67 | { 68 | carry = mergeTrees(carry, rhs.data.front()); 69 | rhs.pop_front(); 70 | ++carryRank; 71 | } 72 | else 73 | { 74 | result.push_back(carry); 75 | mergeHeaps(result, lhs, rhs); 76 | } 77 | } 78 | 79 | BinomialHeap& rest = !lhs.data.empty() ? lhs : rhs; 80 | 81 | while (!rest.data.empty() && carryRank == rest.getRank()) 82 | { 83 | carry = mergeTrees(carry, rest.data.front()); 84 | rest.pop_front(); 85 | ++carryRank; 86 | } 87 | 88 | assert(rest.data.size() == 0 || carryRank < rest.getRank()); 89 | 90 | result.push_back(carry); 91 | result.data.splice(result.data.end(), rest.data); 92 | result.size += rest.size; 93 | 94 | rest.data.clear(); 95 | rest.size = 0; 96 | } 97 | template 98 | void mergeHeaps(BinomialHeap& result, BinomialHeap& lhs, BinomialHeap& rhs) 99 | { 100 | if (lhs.data.empty() || rhs.data.empty()) 101 | { 102 | if (!lhs.data.empty()) 103 | { 104 | result.data.splice(result.data.end(), lhs.data); 105 | result.size += lhs.size; 106 | 107 | lhs.data.clear(); 108 | lhs.size = 0; 109 | } 110 | else 111 | { 112 | result.data.splice(result.data.end(), rhs.data); 113 | result.size += rhs.size; 114 | 115 | rhs.data.clear(); 116 | rhs.size = 0; 117 | } 118 | return; 119 | } 120 | 121 | size_t lhsRank = lhs.getRank(); 122 | size_t rhsRank = rhs.getRank(); 123 | 124 | if (lhsRank != rhsRank) 125 | { 126 | if (lhsRank < rhsRank) 127 | { 128 | result.push_back(lhs.data.front()); 129 | lhs.pop_front(); 130 | } 131 | else 132 | { 133 | result.push_back(rhs.data.front()); 134 | rhs.pop_front(); 135 | } 136 | 137 | mergeHeaps(result, lhs, rhs); 138 | } 139 | else 140 | { 141 | BinomialTree carry = mergeTrees(lhs.data.front(), rhs.data.front()); 142 | lhs.pop_front(); 143 | rhs.pop_front(); 144 | 145 | result.mergeWithCarry(carry, lhsRank + 1, result, lhs, rhs); 146 | } 147 | } 148 | 149 | template 150 | void BinomialHeap::insert(const T& value) 151 | { 152 | BinomialHeap binHeap(value); 153 | 154 | BinomialHeap result; 155 | mergeHeaps(result, *this, binHeap); 156 | *this = std::move(result); 157 | } 158 | template 159 | void BinomialHeap::insert(T&& value) 160 | { 161 | BinomialHeap binHeap(std::move(value)); 162 | 163 | BinomialHeap result; 164 | mergeHeaps(result, *this, binHeap); 165 | *this = std::move(result); 166 | } 167 | 168 | template 169 | const T& BinomialHeap::getMin() const 170 | { 171 | if (!min.has_value()) 172 | { 173 | std::optional tempMin; 174 | for (auto it = data.begin(); it != data.end(); ++it) 175 | if (!tempMin.has_value() || (tempMin > (*it).getMin())) 176 | tempMin = (*it).getMin(); 177 | min = tempMin; 178 | } 179 | 180 | return min.value(); 181 | } 182 | 183 | template 184 | BinomialHeap::BinomialHeap(const T& value) 185 | { 186 | data.push_back(std::move(BinomialTree(value))); 187 | ++size; 188 | } 189 | template 190 | BinomialHeap::BinomialHeap(T&& value) 191 | { 192 | data.push_back(std::move(BinomialTree(std::move(value)))); 193 | ++size; 194 | } 195 | 196 | template 197 | size_t BinomialHeap::countr_zero(size_t n) const 198 | { 199 | if (n == 0) 200 | return 0; 201 | 202 | size_t zerosCount = 0; 203 | while (!(n & 1)) 204 | { 205 | ++zerosCount; 206 | n = n >> 1; 207 | } 208 | return zerosCount; 209 | } 210 | template 211 | size_t BinomialHeap::getRank() const 212 | { 213 | return countr_zero(size); 214 | } 215 | 216 | template 217 | void BinomialHeap::push_back(const BinomialTree& el) 218 | { 219 | size += el.getSize(); 220 | data.push_back(el); 221 | } 222 | template 223 | void BinomialHeap::pop_front() 224 | { 225 | size -= data.front().getSize(); 226 | data.pop_front(); 227 | } -------------------------------------------------------------------------------- /AdvancedDS-Binomial_Heap/BinomialTree.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class BinomialTree; 6 | template 7 | BinomialTree mergeTrees(const BinomialTree& lhs, const BinomialTree& rhs); 8 | 9 | template 10 | class BinomialTree 11 | { 12 | private: 13 | struct Node 14 | { 15 | T data; 16 | Node* next; // right sibling 17 | Node* firstChild; // left child 18 | 19 | Node(const T& data) : data(data), next(nullptr), firstChild(nullptr) 20 | {} 21 | }; 22 | 23 | Node* root; 24 | size_t rank; 25 | size_t size = 0; 26 | 27 | void copyFromRec(Node*& root, Node* otherRoot); 28 | void freeRec(Node* root); 29 | 30 | void copyFrom(const BinomialTree& other); 31 | void moveFrom(BinomialTree&& other); 32 | void free(); 33 | 34 | public: 35 | BinomialTree() = default; 36 | BinomialTree(const T& value); 37 | BinomialTree(T&& value); 38 | 39 | BinomialTree(const BinomialTree& other); 40 | BinomialTree(BinomialTree&& other); 41 | BinomialTree& operator=(const BinomialTree& other); 42 | BinomialTree& operator=(BinomialTree&& other); 43 | ~BinomialTree(); 44 | 45 | size_t getRank() const; 46 | size_t getSize() const; 47 | const T& getMin() const; 48 | 49 | friend BinomialTree mergeTrees(const BinomialTree& lhs, const BinomialTree& rhs); 50 | }; 51 | 52 | template 53 | size_t BinomialTree::getRank() const 54 | { 55 | return rank; 56 | } 57 | template 58 | size_t BinomialTree::getSize() const 59 | { 60 | return size; 61 | } 62 | template 63 | const T& BinomialTree::getMin() const 64 | { 65 | assert(root); 66 | return root->data; 67 | } 68 | 69 | template 70 | BinomialTree mergeTrees(const BinomialTree& lhs, const BinomialTree& rhs) 71 | { 72 | assert(lhs.rank == rhs.rank); 73 | 74 | BinomialTree lhsCopy(lhs); 75 | BinomialTree rhsCopy(rhs); 76 | 77 | if (lhsCopy.root->data > rhsCopy.root->data) 78 | { 79 | auto prevFirstChild = rhsCopy.root->firstChild; 80 | rhsCopy.root->firstChild = lhsCopy.root; 81 | lhsCopy.root->next = prevFirstChild; 82 | 83 | lhsCopy.root = nullptr; 84 | ++rhsCopy.rank; 85 | rhsCopy.size += lhsCopy.size; 86 | 87 | return std::move(rhsCopy); 88 | } 89 | 90 | auto prevFirstChild = lhsCopy.root->firstChild; 91 | lhsCopy.root->firstChild = rhsCopy.root; 92 | rhsCopy.root->next = prevFirstChild; 93 | 94 | rhsCopy.root = nullptr; 95 | ++lhsCopy.rank; 96 | lhsCopy.size += rhsCopy.size; 97 | 98 | return std::move(lhsCopy); 99 | } 100 | 101 | template 102 | BinomialTree::BinomialTree(const T& value) 103 | { 104 | root = new Node(value); 105 | rank = 0; 106 | ++size; 107 | } 108 | template 109 | BinomialTree::BinomialTree(T&& value) 110 | { 111 | root = new Node(std::move(value)); 112 | rank = 0; 113 | ++size; 114 | } 115 | 116 | template 117 | BinomialTree::BinomialTree(const BinomialTree& other) 118 | { 119 | copyFrom(other); 120 | } 121 | template 122 | BinomialTree::BinomialTree(BinomialTree&& other) 123 | { 124 | moveFrom(std::move(other)); 125 | } 126 | template 127 | BinomialTree& BinomialTree::operator=(const BinomialTree& other) 128 | { 129 | if (this != &other) 130 | { 131 | free(); 132 | copyFrom(other); 133 | } 134 | 135 | return *this; 136 | } 137 | template 138 | BinomialTree& BinomialTree::operator=(BinomialTree&& other) 139 | { 140 | if (this != &other) 141 | { 142 | free(); 143 | moveFrom(std::move(other)); 144 | } 145 | 146 | return *this; 147 | } 148 | template 149 | BinomialTree::~BinomialTree() 150 | { 151 | free(); 152 | } 153 | 154 | template 155 | void BinomialTree::copyFrom(const BinomialTree& other) 156 | { 157 | rank = other.rank; 158 | size = other.size; 159 | copyFromRec(root, other.root); 160 | } 161 | template 162 | void BinomialTree::moveFrom(BinomialTree&& other) 163 | { 164 | root = other.root; 165 | other.root = nullptr; 166 | rank = other.rank; 167 | other.rank = 0; 168 | size = other.size; 169 | other.size = 0; 170 | } 171 | template 172 | void BinomialTree::free() 173 | { 174 | freeRec(root); 175 | } 176 | 177 | template 178 | void BinomialTree::copyFromRec(Node*& root, Node* otherRoot) 179 | { 180 | if (!otherRoot) 181 | { 182 | root = nullptr; 183 | return; 184 | } 185 | 186 | root = new Node(*otherRoot); 187 | 188 | copyFromRec(root->next, otherRoot->next); 189 | copyFromRec(root->firstChild, otherRoot->firstChild); 190 | } 191 | template 192 | void BinomialTree::freeRec(Node* root) 193 | { 194 | if (!root) 195 | return; 196 | 197 | freeRec(root->next); 198 | freeRec(root->firstChild); 199 | 200 | delete root; 201 | } -------------------------------------------------------------------------------- /AdvancedDS-Binomial_Heap/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "BinomialHeap.hpp" 2 | 3 | int main() 4 | { 5 | BinomialHeap binHeap1; 6 | binHeap1.insert(2); 7 | binHeap1.insert(7); 8 | binHeap1.insert(4); 9 | binHeap1.insert(8); 10 | binHeap1.insert(12); 11 | binHeap1.insert(10); 12 | binHeap1.insert(5); 13 | binHeap1.insert(21); 14 | binHeap1.insert(9); 15 | binHeap1.insert(11); 16 | binHeap1.insert(1); 17 | 18 | BinomialHeap binHeap2; 19 | binHeap2.insert(6); 20 | binHeap2.insert(14); 21 | binHeap2.insert(3); 22 | 23 | BinomialHeap result; 24 | mergeHeaps(result, binHeap1, binHeap2); 25 | 26 | std::cout << result.getMin(); 27 | } -------------------------------------------------------------------------------- /AdvancedDS-DSW_algorithm/DSW.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) : data(new T(data)), left(nullptr), right(nullptr) 12 | {} 13 | }; 14 | 15 | template 16 | void free(Node* root) 17 | { 18 | if (!root) 19 | return; 20 | 21 | free(root->left); 22 | free(root->right); 23 | 24 | delete root->data; 25 | delete root; 26 | } 27 | 28 | size_t closestLowerPowerOfTwo(size_t n) 29 | { 30 | size_t result = 0; 31 | for (size_t i = n; i >= 1; i--) 32 | { 33 | if ((i & (i - 1)) == 0) 34 | { 35 | result = i; 36 | break; 37 | } 38 | } 39 | return result; 40 | } 41 | 42 | template 43 | size_t height(const Node* root) 44 | { 45 | if (!root) 46 | return 0; 47 | 48 | return 1 + std::max(height(root->left), height(root->right)); 49 | } 50 | 51 | template 52 | void rotateLeft(Node*& root) 53 | { 54 | if (!root || !root->right) 55 | return; 56 | 57 | std::swap(root->data, root->right->data); 58 | Node* originalRight = root->right; 59 | root->right = originalRight->right; 60 | originalRight->right = originalRight->left; 61 | originalRight->left = root->left; 62 | root->left = originalRight; 63 | } 64 | template 65 | void rotateRight(Node*& root) 66 | { 67 | if (!root || !root->left) 68 | return; 69 | 70 | std::swap(root->data, root->left->data); 71 | Node* originalLeft = root->left; 72 | root->left = originalLeft->left; 73 | originalLeft->left = originalLeft->right; 74 | originalLeft->right = root->right; 75 | root->right = originalLeft; 76 | } 77 | 78 | // Balance the tree using the Day-Stout-Warren algorithm. 79 | 80 | template 81 | bool DSW(Node*& root) 82 | { 83 | // 1. Make the tree completely unbalanced (straight) to the right by doing right rotations on all left children. 84 | 85 | Node* currentNode = root; // Start at the root. 86 | size_t nodesCount = 0; // Keep track of the number of nodes in this tree. 87 | 88 | while (currentNode) // Go from the root to the bottom right node. 89 | { 90 | while (currentNode->left) // While we still see a left child. 91 | rotateRight(currentNode); // Get rid of the left child. 92 | if (nodesCount == 0) 93 | root = currentNode; // After we've done all rotations on the root, update the original one. 94 | currentNode = currentNode->right; // Update our current node to it's right child. 95 | nodesCount++; 96 | } 97 | 98 | // If the tree height isn't equal to the number of nodes in the tree, this step failed. 99 | if (nodesCount != height(root)) 100 | return false; 101 | 102 | // 2. Do left rotations and balance the tree (currently - backbone(vine)). 103 | 104 | size_t leavesOnBottomLevel = nodesCount - closestLowerPowerOfTwo(nodesCount) + 1; // The number of nodes on the bottom level. 105 | currentNode = root; 106 | 107 | for (size_t i = 0; i < leavesOnBottomLevel; i++) // For the nodes on the bottom level: 108 | { 109 | if (i == 0) // Rotate the root. 110 | { 111 | rotateLeft(currentNode); 112 | root = currentNode; 113 | } 114 | else // If not the root, rotate the right of the current node. 115 | rotateLeft(currentNode->right); 116 | } 117 | 118 | size_t rotationsCount = nodesCount; 119 | while (rotationsCount > 1) // The number of rotations are reduced twice each time because as we go up the tree, the number of nodes on a certain level are 50% of all nodes (on this level + the upper ones). 120 | { 121 | rotationsCount /= 2; 122 | currentNode = root; 123 | 124 | for (size_t i = 0; i < rotationsCount - 1; i++) // For the length of the vine: 125 | { 126 | if (i == 0) // Rotate the root. 127 | { 128 | rotateLeft(currentNode); 129 | root = currentNode; 130 | } 131 | else 132 | { 133 | currentNode = currentNode->right; // Update the current node. 134 | rotateLeft(currentNode); // Rotate the current node. 135 | } 136 | } 137 | } 138 | 139 | // The tree should be balanced. The height of it (measured in number of edges) is expected to be log(# nodes). 140 | return !(ceil(log(nodesCount)) != height(root) - 1); 141 | } 142 | 143 | int main() 144 | { 145 | Node* root = new Node(10); 146 | Node* n1 = new Node(5); 147 | Node* n2 = new Node(20); 148 | Node* n3 = new Node(7); 149 | Node* n4 = new Node(15); 150 | Node* n5 = new Node(30); 151 | Node* n6 = new Node(25); 152 | Node* n7 = new Node(40); 153 | Node* n8 = new Node(23); 154 | root->left = n1; 155 | root->right = n2; 156 | n1->right = n3; 157 | n2->left = n4; 158 | n2->right = n5; 159 | n5->left = n6; 160 | n5->right = n7; 161 | n6->left = n8; 162 | 163 | std::cout << DSW(root); 164 | free(root); 165 | } 166 | -------------------------------------------------------------------------------- /AdvancedDS-Tiered_Vector/Deque.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | template 6 | class Deque 7 | { 8 | private: 9 | T* data; 10 | 11 | size_t elementsCount; 12 | size_t capacity; 13 | 14 | size_t head; // index of the first element 15 | size_t tail; // index of the last element + 1 16 | 17 | void copyFrom(const Deque&); 18 | void moveFrom(Deque&&); 19 | 20 | void movePosition(size_t&, bool) const; 21 | 22 | public: 23 | Deque() = default; 24 | Deque(const Deque&); 25 | Deque(Deque&&); 26 | Deque& operator=(const Deque&); 27 | Deque& operator=(Deque&&); 28 | ~Deque(); 29 | 30 | void free(); 31 | 32 | void push_back(const T&); 33 | void push_back(T&&); 34 | void push_front(const T&); 35 | void push_front(T&&); 36 | 37 | void pop_back(); 38 | void pop_front(); 39 | 40 | const T& back() const; 41 | const T& front() const; 42 | T& back(); 43 | T& front(); 44 | 45 | const T& operator[](size_t) const; 46 | T& operator[](size_t); 47 | 48 | bool empty() const; 49 | size_t size() const; 50 | 51 | void setCapacity(size_t); 52 | size_t getCapacity() const; 53 | }; 54 | 55 | template 56 | void Deque::push_back(const T& el) 57 | { 58 | data[tail] = el; 59 | movePosition(tail, true); 60 | elementsCount++; 61 | } 62 | template 63 | void Deque::push_back(T&& el) 64 | { 65 | data[tail] = std::move(el); 66 | movePosition(tail, true); 67 | elementsCount++; 68 | } 69 | template 70 | void Deque::push_front(const T& el) 71 | { 72 | movePosition(head, false); 73 | data[head] = el; 74 | elementsCount++; 75 | } 76 | template 77 | void Deque::push_front(T&& el) 78 | { 79 | movePosition(head, false); 80 | data[head] = std::move(el); 81 | elementsCount++; 82 | } 83 | 84 | template 85 | void Deque::pop_back() 86 | { 87 | if (empty()) 88 | throw std::length_error("Empty deque!"); 89 | 90 | movePosition(tail, false); 91 | elementsCount--; 92 | } 93 | template 94 | void Deque::pop_front() 95 | { 96 | if (empty()) 97 | throw std::length_error("Empty deque!"); 98 | 99 | movePosition(head, true); 100 | elementsCount--; 101 | } 102 | 103 | template 104 | const T& Deque::back() const 105 | { 106 | if (empty()) 107 | throw std::length_error("Empty deque!"); 108 | 109 | size_t lastElement = tail; movePosition(lastElement, false); 110 | return data[lastElement]; 111 | } 112 | template 113 | const T& Deque::front() const 114 | { 115 | if (empty()) 116 | throw std::length_error("Empty deque!"); 117 | 118 | return data[head]; 119 | } 120 | template 121 | T& Deque::back() 122 | { 123 | if (empty()) 124 | throw std::length_error("Empty deque!"); 125 | 126 | size_t lastElement = tail; movePosition(lastElement, false); 127 | return data[lastElement]; 128 | } 129 | template 130 | T& Deque::front() 131 | { 132 | if (empty()) 133 | throw std::length_error("Empty deque!"); 134 | 135 | return data[head]; 136 | } 137 | 138 | template 139 | const T& Deque::operator[](size_t index) const 140 | { 141 | assert(index < elementsCount); 142 | return data[(head + index) % capacity]; 143 | } 144 | template 145 | T& Deque::operator[](size_t index) 146 | { 147 | assert(index < elementsCount); 148 | return data[(head + index) % capacity]; 149 | } 150 | 151 | template 152 | bool Deque::empty() const 153 | { 154 | return elementsCount == 0; 155 | } 156 | template 157 | size_t Deque::size() const 158 | { 159 | return elementsCount; 160 | } 161 | 162 | template 163 | void Deque::setCapacity(size_t capacity) 164 | { 165 | delete[] data; 166 | 167 | elementsCount = 0; 168 | this->capacity = capacity; 169 | data = new T[capacity]; 170 | 171 | head = tail = 0; 172 | } 173 | template 174 | size_t Deque::getCapacity() const 175 | { 176 | return capacity; 177 | } 178 | 179 | template 180 | void Deque::movePosition(size_t& currPosition, bool moveForward) const 181 | { 182 | if (moveForward) 183 | { 184 | (++currPosition) %= capacity; 185 | } 186 | else 187 | { 188 | if (currPosition == 0) 189 | currPosition = capacity - 1; 190 | else 191 | currPosition--; 192 | } 193 | } 194 | 195 | template 196 | Deque::Deque(const Deque& other) 197 | { 198 | copyFrom(other); 199 | } 200 | template 201 | Deque::Deque(Deque&& other) 202 | { 203 | moveFrom(std::move(other)); 204 | } 205 | template 206 | Deque& Deque::operator=(const Deque& other) 207 | { 208 | if (this != &other) 209 | { 210 | free(); 211 | copyFrom(other); 212 | } 213 | return *this; 214 | } 215 | template 216 | Deque& Deque::operator=(Deque&& other) 217 | { 218 | if (this != &other) 219 | { 220 | free(); 221 | moveFrom(std::move(other)); 222 | } 223 | return *this; 224 | } 225 | template 226 | Deque::~Deque() 227 | { 228 | free(); 229 | } 230 | 231 | template 232 | void Deque::free() 233 | { 234 | delete[] data; 235 | data = nullptr; 236 | } 237 | template 238 | void Deque::copyFrom(const Deque& other) 239 | { 240 | elementsCount = other.elementsCount; 241 | capacity = other.capacity; 242 | 243 | head = other.head; 244 | tail = other.tail; 245 | 246 | data = new T[capacity]; 247 | for (size_t i = 0; i < capacity; i++) 248 | data[i] = other.data[i]; 249 | } 250 | template 251 | void Deque::moveFrom(Deque&& other) 252 | { 253 | elementsCount = other.elementsCount; 254 | capacity = other.capacity; 255 | 256 | head = other.head; 257 | tail = other.tail; 258 | 259 | other.elementsCount = other.capacity = other.head = other.tail = 0; 260 | 261 | data = other.data; 262 | other.data = nullptr; 263 | } 264 | -------------------------------------------------------------------------------- /AdvancedDS-Tiered_Vector/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "Tests.hpp" 2 | 3 | int main() 4 | { 5 | runTests(); 6 | } -------------------------------------------------------------------------------- /AdvancedDS-Tiered_Vector/Tests.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TieredVector.hpp" 3 | 4 | bool testInitialize() 5 | { 6 | TieredVector tv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 7 | if (tv.size() != 10) 8 | return false; 9 | 10 | for (size_t i = 0; i < tv.size(); i++) 11 | if (tv[i] != i) 12 | return false; 13 | return true; 14 | } 15 | 16 | bool testInsertBack() 17 | { 18 | TieredVector tv; 19 | 20 | for (size_t i = 0; i <= 200; i++) 21 | tv.insert(i); 22 | if (tv.size() != 201) 23 | return false; 24 | 25 | for (size_t i = 0; i < tv.size(); i++) 26 | if (tv[i] != i) 27 | return false; 28 | return true; 29 | } 30 | 31 | bool testInsertFront() 32 | { 33 | TieredVector tv; 34 | 35 | for (size_t i = 200; i != 0; i--) 36 | tv.insert(i); 37 | if (tv.size() != 200) 38 | return false; 39 | 40 | for (size_t i = 0; i < tv.size(); i++) 41 | if (tv[i] != i + 1) 42 | return false; 43 | return true; 44 | } 45 | 46 | bool testInsertBackAndFront() 47 | { 48 | TieredVector tv; 49 | 50 | for (size_t i = 0; i < 150; i += 2) 51 | { 52 | tv.insert(i); 53 | tv.insert(300 - i); 54 | tv.insert(300 - i - 1); 55 | tv.insert(i + 1); 56 | } 57 | tv.insert(150); 58 | if (tv.size() != 301) 59 | return false; 60 | 61 | for (size_t i = 0; i < tv.size(); i++) 62 | if (tv[i] != i) 63 | return false; 64 | return true; 65 | } 66 | 67 | bool testEraseBackAndFront() 68 | { 69 | TieredVector tv; 70 | 71 | for (size_t i = 0; i < 300; i++) 72 | tv.insert(i); 73 | 74 | for (size_t i = 0; i < 50; i++) 75 | { 76 | tv.erase(i); 77 | tv.erase(299 - i); 78 | } 79 | 80 | if (tv.size() != 200) 81 | return false; 82 | 83 | for (size_t i = 0; i < tv.size(); i++) 84 | if (tv[i] != i + 50) 85 | return false; 86 | return true; 87 | } 88 | 89 | bool testEraseAllEvenNumbers() 90 | { 91 | TieredVector tv; 92 | 93 | for (size_t i = 0; i < 300; i++) 94 | tv.insert(i); 95 | 96 | for (size_t i = 0; i < 300; i += 2) 97 | tv.erase(i); 98 | 99 | if (tv.size() != 150) 100 | return false; 101 | 102 | for (size_t i = 0; i < tv.size(); i++) 103 | if (tv[i] % 2 != 1) 104 | return false; 105 | return true; 106 | } 107 | 108 | bool testEraseRandom() 109 | { 110 | TieredVector tv; 111 | 112 | for (size_t i = 0; i < 300; i++) 113 | tv.insert(i); 114 | 115 | for (size_t i = 0; i < 50; i++) 116 | { 117 | tv.erase(i); 118 | tv.erase(100 + i); 119 | tv.erase(299 - i); 120 | } 121 | 122 | if (tv.size() != 150) 123 | return false; 124 | 125 | for (size_t i = 0; i < 50; i++) 126 | if (tv[i] != i + 50) 127 | return false; 128 | for (size_t i = 0; i < 50; i++) 129 | if (tv[i + 50] != i + 150) 130 | return false; 131 | for (size_t i = 0; i < 50; i++) 132 | if (tv[i + 100] != i + 200) 133 | return false; 134 | return true; 135 | } 136 | 137 | bool testMakeEmptyAndThenRefill() 138 | { 139 | TieredVector tv; 140 | for (size_t i = 1; i <= 200; i++) 141 | tv.insert(i); 142 | if (tv.size() != 200) 143 | return false; 144 | 145 | for (size_t i = 200; i != 0; i--) 146 | tv.erase(i); 147 | if (tv.size() != 0) 148 | return false; 149 | 150 | for (size_t i = 200; i != 0; i--) 151 | tv.insert(i); 152 | if (tv.size() != 200) 153 | return false; 154 | 155 | return true; 156 | } 157 | 158 | bool testMakeEmptyAndThenRefill2() 159 | { 160 | TieredVector tv; 161 | for (size_t i = 1; i <= 200; i++) 162 | tv.insert(i); 163 | if (tv.size() != 200) 164 | return false; 165 | 166 | for (size_t i = 1; i <= 200; i++) 167 | tv.erase(i); 168 | if (tv.size() != 0) 169 | return false; 170 | 171 | for (size_t i = 200; i != 0; i--) 172 | tv.insert(i); 173 | if (tv.size() != 200) 174 | return false; 175 | 176 | return true; 177 | } 178 | 179 | bool testCopy() 180 | { 181 | TieredVector tv1; 182 | for (size_t i = 10; i < 20; i++) 183 | tv1.insert(i); 184 | for (int i = 9; i >= 0; i--) 185 | tv1.insert(i); 186 | 187 | TieredVector tv2(tv1); 188 | 189 | TieredVector tv3; 190 | tv3.insert(1); 191 | tv3 = tv2; 192 | 193 | tv2.erase(5); 194 | 195 | if (tv1.size() != 20) 196 | return false; 197 | 198 | for (size_t i = 0; i < 20; i++) 199 | if (tv1[i] != i) 200 | return false; 201 | 202 | if (tv2.size() != 19) 203 | return false; 204 | 205 | for (size_t i = 0; i < 19; i++) 206 | { 207 | if (i < 5 && tv2[i] != i) 208 | return false; 209 | if (i >= 5 && tv2[i] - 1 != i) 210 | return false; 211 | } 212 | 213 | tv3.insert(20); 214 | if (tv3.size() != 21) 215 | return false; 216 | 217 | for (size_t i = 0; i < 21; i++) 218 | if (tv3[i] != i) 219 | return false; 220 | 221 | if (tv1.size() != 20) 222 | return false; 223 | 224 | for (size_t i = 0; i < 20; i++) 225 | if (tv1[i] != i) 226 | return false; 227 | 228 | return true; 229 | } 230 | 231 | void runTests() 232 | { 233 | std::cout << "Test 1: " << testInitialize() << std::endl; 234 | std::cout << "Test 2: " << testInsertBack() << std::endl; 235 | std::cout << "Test 3: " << testInsertFront() << std::endl; 236 | std::cout << "Test 4: " << testInsertBackAndFront() << std::endl; 237 | std::cout << "Test 5: " << testEraseBackAndFront() << std::endl; 238 | std::cout << "Test 6: " << testEraseAllEvenNumbers() << std::endl; 239 | std::cout << "Test 7: " << testEraseRandom() << std::endl; 240 | std::cout << "Test 8: " << testMakeEmptyAndThenRefill() << std::endl; 241 | std::cout << "Test 9: " << testMakeEmptyAndThenRefill2() << std::endl; 242 | std::cout << "Test 10: " << testCopy() << std::endl; 243 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Код от семинарите по "Структури от данни и програмиране", зимен семестър 2024/2025, спец. "Информатика" ## 2 | 3 | - **Тема 1** : Анализ на итеративни алгоритми. Нотации. Анализ на сложност и стабилност на сортиращи алгоритми (Bubble sort, Selection sort, Insertion sort). 4 | - **Тема 2** : Анализ на рекурсивни алгоритми. Merge sort. Quick sort. Сортиране в линейно време. Counting sort. 5 | - **Тема 3** : Увод в линейните структури от данни. Вектор (Динамичен масив). Сложност на операциите му. Амортизирана сложност. Locality. New expression, operator new, placement new. Delete, operator delete. 6 | - **Тема 4** : Свързан списък. Едносвързан списък (Singly Linked List). Двусвързан списък (Doubly Linked List). Сортиране на списъци. 7 | - **Тема 5** : Абстрактна структура от данни Deque. Стек. Опашка. 8 | - **Тема 6** : Увод в йерархичните структури от данни. Дървета. Двоични дървета. N-ични дървета. 9 | - **Тема 7** : Двоично наредено дърво за търсене (Binary search tree). Итератор за дърво. 10 | - **Тема 8** : Самобалансиращи се дървета. Видове. AVL + DoS (select и rank) дърво. 11 | - **Тема 9** : Двоична пирамида (Binary Heap). Приоритетна опашка (Priority queue). Сортиращ алгоритъм Heapsort (Пирамидално сортиране). 12 | - **Тема 10** : Set и Map. Хеш таблици. Справяне с колизии. 13 | - **Тема 11** : Алгоритми върху графи. Обхождания на графи (BFS и DFS). Търсене на цикъл в граф. Намиране на свързани компоненти. Топологична сортировка. 14 | - **Тема 12** : Тегловни графи. Най- къс път в тегловен граф. Алгоритми на Dijkstra и Bellman-Ford. 15 | - **Тема 13** : Тегловни графи. Минимално покриващо дърво. Алгоритми на Prim и Kruskal. 16 | 17 | ## Структури от данни 2 - ФМИ 18 | - **Тема** : Алгоритъм DSW - алгоритъм за балансиране по височина на двоично наредено дърво. 19 | - **Тема** : Самобалансиращи се дървета - AVL дърво (поддържащо DoS). 20 | - **Тема** : Биномна пирамида (Binomial Heap). 21 | - **Тема** : Deque (Double-ended queue). 22 | - **Тема** : Слоест вектор (Tiered vector) - структура, поддържаща операциите индексиране, търсене на елемент, добавяне/премахване на елемент за времена съответно $\Theta(1), \Theta(\log n), \Theta(\sqrt{n})$. 23 | 24 | ## Other (advanced) topics 25 | - **Тема** : Intel intrinsics - оптимизация на Двоично търсене. 26 | - **Тема** : Memory allocator. 27 | - **Тема** : Timer scheduler - система за управление на еднократни и повтарящи се таймери. 28 | -------------------------------------------------------------------------------- /Sem_01/Complexity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // O(n) 5 | int sum(const int* arr, size_t len) 6 | { 7 | int sum = 0; 8 | for (size_t i = 0; i < len; i++) 9 | { 10 | sum += arr[i]; 11 | } 12 | return sum; 13 | } 14 | 15 | // O(n) 16 | bool contains(const int* arr, size_t len, int el) 17 | { 18 | for (size_t i = 0; i < len; i++) 19 | { 20 | if (arr[i] == el) 21 | { 22 | return true; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | // O(n) 29 | size_t f1(unsigned n) // sum = 1 + 2 + ... + n = n(n+1)/2 30 | { 31 | size_t sum = 0; 32 | for (size_t i = 1; i <= n; i++) 33 | { 34 | sum += i; 35 | } 36 | return sum; 37 | } 38 | // O(1) 39 | size_t f1Better(unsigned n) 40 | { 41 | return n * (n + 1) / 2; 42 | } 43 | 44 | // O(log(b)) 45 | unsigned power(unsigned a, unsigned b) 46 | { 47 | unsigned pow = 1; 48 | while (b) 49 | { 50 | if (b & 1) 51 | { 52 | pow = pow * a; 53 | --b; 54 | } 55 | a = a * a; 56 | b = b / 2; 57 | } 58 | return pow; 59 | } 60 | 61 | // O(log(1) + log(2) + ... + log(n)) = O(log(n!)) = O(nlog(n)) 62 | unsigned f2(unsigned n) // res = 2^0 + 2^1 + ... + 2^n = 2^(n+1) - 1 63 | { 64 | unsigned res = 0; 65 | unsigned iter = 2; 66 | for (size_t i = 0; i <= n; i++) 67 | { 68 | res += power(iter, i); 69 | } 70 | return res; 71 | } 72 | // O(1) 73 | unsigned f2Better(unsigned n) 74 | { 75 | return (1 << (n + 1)) - 1; 76 | } 77 | 78 | // O(log(n)) 79 | bool f3(unsigned n) // checks whether n is a power of two 80 | { 81 | while (n > 1) 82 | { 83 | if (n % 2 == 1) 84 | { 85 | return false; 86 | } 87 | n /= 2; 88 | } 89 | return true; 90 | } 91 | // O(1) 92 | bool f3Better(unsigned n) 93 | { 94 | if (n != 0) 95 | { 96 | return !(n & (n - 1)); 97 | } 98 | return false; 99 | } 100 | 101 | // O(n) 102 | void f4(unsigned n) 103 | { 104 | for (size_t i = 1; i <= n; i *= 2) 105 | { 106 | for (size_t j = 1; j <= i; j++) 107 | { 108 | // O(1) 109 | } 110 | } 111 | } 112 | 113 | // O(nlog(n)) 114 | void f5(unsigned n) 115 | { 116 | for (size_t i = 1; i <= n; i++) 117 | { 118 | for (size_t j = 1; j <= n; j += i) 119 | { 120 | // O(1) 121 | } 122 | } 123 | } 124 | 125 | int main() 126 | { 127 | 128 | } 129 | -------------------------------------------------------------------------------- /Sem_01/README.md: -------------------------------------------------------------------------------- 1 | # Анализ на алгоритми 2 | 3 | Да бъде **анализиран** даден алгоритъм означава да бъде доказано, че той е **коректен**, и ако е коректен, да бъде 4 | изчислено колко **ресурси използва** - за всеки възможен вход - като ресурсите са основно **време** и **памет**. 5 | 6 | В този курс ще разглеждаме само втората част от анализа на алгоритми - **сложността**. 7 | 8 | **“Сложност на алгоритъм”** е мярка за това **колко ресурси полза този алгоритъм.** За какви ресурси става дума? 9 | 1. **Време.** Искаме алгоритмите ни да работят бързо. 10 | В този смисъл, “качествен алгоритъм” е алгоритъм, който работи бързо върху *всички* входове. 11 | 2. **Памет.** Искаме алгоритмите ни да ползват малко памет. 12 | В този смисъл, “качествен алгоритъм” е алгоритъм, който ползва малко памет върху *всички* входове. 13 | 14 | **Сложността по време** е мярка, която ни казва как нараства времето за изпълнение на даден алгоритъм, когато големината на входа му клони към *безкрайност*. 15 | **Сложността по памет** е мярка, която ни казва как нараства паметта, която даден алгоритъм ползва, когато големината на входа му клони към *безкрайност*. 16 | 17 | Когато разглеждаме сложността по време на алгоритми, разглеждаме *три* случая: 18 | 19 | - Най-добър случай (**Best case**) 20 | - Среден случай (**Average case**) 21 | - Най-лош случай (**Worst case**) 22 | 23 | Коя е най-ценната величина от трите? 24 | **Средният случай!** ..*Но:* 25 | - Средното време за работа обикновено се определя трудно. 26 | - Ще се фокусираме върху времето за работа **в най-лошия случай**. 27 | 28 | ![alt_text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_01/images/AsymptoticNotations.png) 29 | -------------------------------------------------------------------------------- /Sem_01/Sorting algorithms/BubbleSort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | void bubbleSort(T* arr, const unsigned len) 6 | { 7 | unsigned end = len - 1; 8 | for (size_t i = 0; i < len; i++) 9 | { 10 | unsigned lastSwappedIndex = 0; 11 | 12 | for (size_t j = 0; j < end; j++) 13 | { 14 | if (arr[j] > arr[j + 1]) 15 | { 16 | std::swap(arr[j], arr[j + 1]); 17 | lastSwappedIndex = j; 18 | } 19 | } 20 | 21 | end = lastSwappedIndex; 22 | if (lastSwappedIndex == 0) 23 | { 24 | return; 25 | } 26 | } 27 | } 28 | 29 | int main() 30 | { 31 | int arr[] = { 7, 6, 5, 4, 3, 2, 1 }; 32 | 33 | bubbleSort(arr, 7); 34 | 35 | for (size_t i = 0; i < 7; i++) 36 | { 37 | std::cout << arr[i] << " "; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /Sem_01/Sorting algorithms/InsertionSort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | void insertionSortNaive(T* arr, const unsigned len) 6 | { 7 | for (size_t i = 1; i < len; i++) 8 | { 9 | size_t elIndex = i; 10 | while (elIndex >= 0 && arr[elIndex] < arr[elIndex - 1]) 11 | { 12 | std::swap(arr[elIndex], arr[elIndex - 1]); 13 | elIndex--; 14 | } 15 | } 16 | } 17 | 18 | template 19 | void insertionSort(T* arr, const unsigned len) 20 | { 21 | for (size_t i = 1; i < len; i++) 22 | { 23 | T el = arr[i]; 24 | int index = i - 1; 25 | while (index >= 0 && el < arr[index]) 26 | { 27 | arr[index + 1] = arr[index]; 28 | index--; 29 | } 30 | arr[index + 1] = el; 31 | } 32 | } 33 | 34 | int main() 35 | { 36 | int arr[] = { 7, 6, 5, 4, 3, 2, 1 }; 37 | 38 | insertionSort(arr, 7); 39 | 40 | for (size_t i = 0; i < 7; i++) 41 | { 42 | std::cout << arr[i] << " "; 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /Sem_01/Sorting algorithms/README.md: -------------------------------------------------------------------------------- 1 | ## Итеративни сортиращи алгоритми 2 | 3 | ||Bubble sort|Selection sort|Insertion sort| 4 | |--|--|--|--| 5 | |Най-добър случай (Best case) |O(n) |O(n^2) |O(n) | 6 | |Среден случай (Average case) |O(n^2) |O(n^2) |O(n^2) | 7 | |Най-лош случай (Worst case) |O(n^2) |O(n^2) |O(n^2) | 8 | |Памет |O(1) |O(1) |O(1) | 9 | |Стабилност |:heavy_check_mark: |:x: |:heavy_check_mark: | 10 | |Предимства |Използван е за да се въведе концепцията за сортиращ алгоритъм |Прави инимален брой размени (swaps) - в най- лошия случай - n-1 |Добър за сортиране на масиви, чиито елементи са близко до правилните си позиции. Добър за сортиране на "малки" масиви. | 11 | -------------------------------------------------------------------------------- /Sem_01/Sorting algorithms/SelectionSort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | void selectionSort(T* arr, const unsigned len) 6 | { 7 | for (size_t i = 0; i < len - 1; i++) 8 | { 9 | size_t minElementIndex = i; 10 | 11 | for (size_t j = i + 1; j < len; j++) 12 | { 13 | if (arr[j] < arr[minElementIndex]) 14 | { 15 | minElementIndex = j; 16 | } 17 | } 18 | 19 | if (i != minElementIndex) 20 | { 21 | std::swap(arr[i], arr[minElementIndex]); 22 | } 23 | } 24 | } 25 | 26 | int main() 27 | { 28 | int arr[] = { 7, 6, 5, 4, 3, 2, 1 }; 29 | 30 | selectionSort(arr, 7); 31 | 32 | for (size_t i = 0; i < 7; i++) 33 | { 34 | std::cout << arr[i] << " "; 35 | } 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /Sem_01/images/AsymptoticNotations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_01/images/AsymptoticNotations.png -------------------------------------------------------------------------------- /Sem_02/README.md: -------------------------------------------------------------------------------- 1 | # Анализ на рекурсивни алгоритми 2 | 3 | Анализът на сложността на **рекурсивни алгоритми** се извършва чрез решаване на рекурентни уравнения. 4 | При изследването на алгоритмите не се интересуваме от точен израз за сложността. Интересува ни асимптотиката. Съответно, няма да се опитваме да решаваме рекурентните уравнения точно, а **ще търсим асимптотиката на решението**. 5 | 6 | *Пример - Числа на Фибоначи:* 7 | $T(n) = T(n - 1) + T(n - 2)$ 8 | Начални условия: 9 | $T(0) = 1 \\ \\ \\ T(1) = 1$ 10 | Да решим рекурентното уравнение означава да намерим затворена формула, т.е. такава, в която няма рекурсивно извикване отдясно: 11 | $\displaystyle T(n) = A \cdot \left( \frac{1 + \sqrt{5}}{2} \right)^n + B \cdot \left( \frac{1 - \sqrt{5}}{2} \right)^n$ 12 | Константите $A$ и $B$ не ни интересуват, тъй като търсим само асимптотиката на $T(n)$. 13 | 14 | --- 15 | 16 | ## Как решаваме рекурентни уравнения? 17 | - Налучкване и доказване 18 | - Развиване 19 | - Master theorem 20 | - Други 21 | 22 | --- 23 | 24 | ## Сложност на рекурсивни алгоритми 25 | Връзката между рекурентните уравнения и рекурсивните алгоритми е трикова. От една страна, всяко рекурентно уравнение е рекурсивен алгоритъм, който за всеки достатъчно голям вход-число връща изход число след пресмятане, което включва рекурсия. От друга страна, сложността на рекурсивните алгоритми по правило се описва от рекурентни уравнения. При изследването на сложността на рекурсивни алгоритми чрез рекурентни уравнения е важно да се прави разлика между алгоритъма, чиято сложност изследваме, и рекурентното уравнение, което описва тази сложност. На свой ред то също е рекурсивен алгоритъм, но е съвършено различен обект от първоначалния алгоритъм. Като пример да разгледаме следното рекурентното уравнение: 26 | $\displaystyle T(n) = 1, \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ n = 0$ 27 | $\displaystyle T(n) = n \cdot T(n-1) + 1, \\ n \ne 0$ 28 | 29 | То описва сложността на алгоритъм от вида: 30 | ```c++ 31 | unsigned long long f(unsigned n) 32 | { 33 | if (n == 0) 34 | { 35 | return 1; 36 | } 37 | 38 | unsigned long long s = n; 39 | for (size_t i = 1; i <= n; i++) 40 | { 41 | s += f(n - 1); 42 | } 43 | return s; 44 | } 45 | ``` 46 | 47 | Ако пресметнем рекурентното уравнение, ще получим $\displaystyle T(n) = \Theta(n!)$, т.е. това е алгоритъм, който няма да приключи никога дори за малки стойности на $n$. От друга страна, ако самото рекурентно уравнение бъде пренаписано като рекурсивен алгоритъм, то той ще има линейна сложност. 48 | 49 | --- 50 | 51 | ### Задача 1 - Анализ на линейно търсене 52 | $\displaystyle T(n) = T(n-1) + \Theta(1) = T(n-2) + 2\cdot \Theta(1) = T(n-3) + 3\cdot \Theta(1) = ... = n\cdot \Theta(1) = \Theta(n)$ 53 | 54 | --- 55 | 56 | ### Задача 2 57 | Каква е сложността на следния алгоритъм: 58 | ```c++ 59 | int f(int n) 60 | { 61 | if (n == 0) 62 | { 63 | return 7; 64 | } 65 | return 3 * f(n - 1) + 2 * f(n - 1) + f(n - 1); 66 | } 67 | ``` 68 | 69 | $\displaystyle T(n) = 3\cdot T(n-1) + \Theta(1)$ 70 | 71 | ![alt_text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_02/images/Task02.png) 72 | 73 | --- 74 | 75 | ### Задача 3 - Анализ на Фибоначи 76 | ```c++ 77 | unsigned long long fibb(unsigned n) 78 | { 79 | if (n == 0 || n == 1) 80 | { 81 | return 1; 82 | } 83 | return fibb(n - 1) + fibb(n - 2); 84 | } 85 | ``` 86 | 87 | $\displaystyle T(n) = T(n-1) + T(n-2)$ 88 | 89 | За да намерим $n$-тото число на Фибоначи, алгоритъмът ще извърши точно толкова стъпки, колкото е самото число. Това е така, защото алгоритъмът акумулира резултата само чрез "събиране с 1-ца". 90 | 91 | ![alt_text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_02/images/Task03.png) 92 | 93 | --- 94 | 95 | ### Задача 4 - "Разделяй и владей" 96 | - Разделяме задачата на подзадачи 97 | - Решаваме по-малките задачи 98 | - Акумулираме решенията 99 | 100 | *Пример - Линейно търсене по схемата "Разделяй и владей" (в несортиран масив!):* 101 | ```c++ 102 | bool linearSearchDivideAndConquer(const int* arr, int len, int x) 103 | { 104 | if (len == 0) 105 | { 106 | return false; 107 | } 108 | if (len == 1) 109 | { 110 | return arr[0] == x; 111 | } 112 | return linearSearchDivideAndConquer(arr, len / 2, x) || linearSearchDivideAndConquer(arr + len / 2, len - len / 2, x); 113 | } 114 | ``` 115 | 116 | $\displaystyle T(n) = 2 \cdot T\left(\frac{n}{2}\right) + \Theta(1) = \Theta(n)$ 117 | 118 | -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/CountingSort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Student 5 | { 6 | std::string name; 7 | int grade; 8 | }; 9 | 10 | void countingSortForGrades(Student* arr, int len, int range) 11 | { 12 | int* enumerativeArray = new int[range] { 0 }; 13 | Student* result = new Student[len]; 14 | 15 | for (int i = 0; i < len; i++) 16 | { 17 | enumerativeArray[arr[i].grade - 2]++; 18 | } 19 | 20 | for (int i = 0; i < range - 1; i++) 21 | { 22 | enumerativeArray[i + 1] += enumerativeArray[i]; 23 | } 24 | 25 | for (int i = len - 1; i >= 0; i--) 26 | { 27 | Student currentStudent = arr[i]; 28 | int index = enumerativeArray[currentStudent.grade - 2]--; 29 | result[index - 1] = currentStudent; 30 | } 31 | 32 | for (int i = 0; i < len; i++) 33 | { 34 | arr[i] = result[i]; 35 | } 36 | 37 | delete[] result; 38 | delete[] enumerativeArray; 39 | } 40 | 41 | int main() 42 | { 43 | Student arr[] = { { "Petur", 4 }, { "Ivan", 6 }, { "Paul", 4 }, { "Vladimir", 5 }, { "Petq", 5 } }; 44 | countingSortForGrades(arr, 5, 5); 45 | 46 | for (size_t i = 0; i < 5; i++) 47 | { 48 | std::cout << "Name: " << arr[i].name << ", grade: " << arr[i].grade << std::endl; 49 | } 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/MergeSort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | void merge(T* arr1, size_t len1, T* arr2, size_t len2, T* result) 5 | { 6 | unsigned arr1Iter = 0; 7 | unsigned arr2Iter = 0; 8 | unsigned resultIter = 0; 9 | 10 | while (arr1Iter < len1 && arr2Iter < len2) 11 | { 12 | if (arr1[arr1Iter] <= arr2[arr2Iter]) 13 | { 14 | result[resultIter++] = arr1[arr1Iter++]; 15 | } 16 | else 17 | { 18 | result[resultIter++] = arr2[arr2Iter++]; 19 | } 20 | } 21 | 22 | while (arr1Iter < len1) 23 | { 24 | result[resultIter++] = arr1[arr1Iter++]; 25 | } 26 | 27 | while (arr2Iter < len2) 28 | { 29 | result[resultIter++] = arr2[arr2Iter++]; 30 | } 31 | } 32 | 33 | template 34 | void mergeSortStep(T* arr, size_t len, T* bufferArray) 35 | { 36 | if (len <= 1) 37 | { 38 | return; 39 | } 40 | 41 | unsigned midIndex = len / 2; 42 | 43 | mergeSortStep(arr, midIndex, bufferArray); 44 | mergeSortStep(arr + midIndex, len - midIndex, bufferArray); 45 | 46 | merge(arr, midIndex, arr + midIndex, len - midIndex, bufferArray); 47 | 48 | for (size_t i = 0; i < len; i++) 49 | { 50 | arr[i] = bufferArray[i]; 51 | } 52 | } 53 | 54 | template 55 | void mergeSort(T* arr, size_t len) 56 | { 57 | if (!arr || len == 0) 58 | { 59 | return; 60 | } 61 | 62 | T* bufferArray = new T[len]; 63 | mergeSortStep(arr, len, bufferArray); 64 | delete[] bufferArray; 65 | } 66 | 67 | int main() 68 | { 69 | int arr[8] = {170, 45, 75, 90, 802, 24, 2, 66}; 70 | mergeSort(arr, 8); 71 | 72 | for (int i = 0; i < 8; i++) 73 | { 74 | std::cout << arr[i] << " "; 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/QuickSort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | int partition(T* arr, int len) 6 | { 7 | T pivot = arr[len / 2]; 8 | 9 | int i = 0; 10 | int j = len - 1; 11 | 12 | while (true) 13 | { 14 | while (arr[i] < pivot) 15 | { 16 | i++; 17 | } 18 | while (arr[j] > pivot) 19 | { 20 | j--; 21 | } 22 | 23 | if (arr[i] == arr[j]) 24 | { 25 | i++; 26 | } 27 | if (i < j) 28 | { 29 | std::swap(arr[i], arr[j]); 30 | } 31 | else 32 | { 33 | return j; 34 | } 35 | } 36 | } 37 | 38 | template 39 | void quickSort(T* arr, int len) 40 | { 41 | if (len <= 1) 42 | { 43 | return; 44 | } 45 | 46 | int pivotIndex = partition(arr, len); 47 | 48 | quickSort(arr, pivotIndex); 49 | quickSort(arr + pivotIndex + 1, len - pivotIndex - 1); 50 | } 51 | 52 | int main() 53 | { 54 | int arr[6] = { 6, 70, 7, 3, 4, 12 }; 55 | quickSort(arr, 6); 56 | 57 | for (int i = 0; i < 6; i++) 58 | { 59 | std::cout << arr[i] << " "; 60 | } 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/README.md: -------------------------------------------------------------------------------- 1 | # Рекурсивни сортиращи алгоритми 2 | 3 | ## Merge sort 4 | Рекурсивен сортиращ алгоритъм, изграден по алгоритмичната схема "**Разделяй и владей**". 5 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_02/Sorting%20algorithms%20(Pt.%202)/images/MergeSort.png) 6 | ![alt_text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_02/Sorting%20algorithms%20(Pt.%202)/images/MergeSortComplexity.png) 7 | 8 | --- 9 | 10 | ## Quick sort 11 | Рекурсивен сортиращ алгоритъм, изграден по алгоритмичната схема "**Разделяй и владей**". 12 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_02/Sorting%20algorithms%20(Pt.%202)/images/QuickSort.png) 13 | 14 | ### Сложност по памет на Quick Sort: 15 | $\displaystyle \Theta(1)$ 16 | 17 | ### Сложност по време на Quick Sort: 18 | **Best case и Average case** (пивотът е среден по големина елемент в масива): 19 | $\displaystyle T(n) = 2 \cdot T\left(\frac{n}{2}\right) + \Theta(n) = \Theta(n\log n)$ 20 | **Worst case** (пивотът е минимален/максимален елемент в масива): 21 | $\displaystyle T(n) = T(n-1) + T(0) + \Theta(n) = T(n-1) + \Theta(n) = T(n-2) + 2\cdot \Theta(n) = ... = n\cdot \Theta(n) = \Theta(n^2)$ 22 | 23 | --- 24 | 25 | ||Merge sort|Quick sort| 26 | |--|--|--| 27 | |Най-добър случай (Best case) |$\Theta(n\log n)$ |$\Theta(n\log n)$ | 28 | |Среден случай (Average case) |$\Theta(n\log n)$ |$\Theta(n\log n)$ | 29 | |Най-лош случай (Worst case) |$\Theta(n\log n)$ |$\Theta(n^2)$ | 30 | |Памет |$\Theta(n)$ |$\Theta(1)$ | 31 | |Стабилност |:heavy_check_mark: |:x: | 32 | |Недостатъци |Допълнителна памет| Възможност за квадратична по време сложност | 33 | 34 | **Теорема** - Не съществува сортиращ алгоритъм, **базиран на директни сравнения**, който в най- лошия случай прави < $n\log n$ стъпки. 35 | 36 | --- 37 | 38 | ## Сортиране в линейно време. Counting sort 39 | Сортирането на произволни елементи, при които не можем да четем конкретните им стойности, а можем само да ги сравняваме помежду им, не може да се извършва асимптотично по-бързо от $\Theta(n \log n)$. Тази долна граница обаче не важи, когато има ограничения върху възможните стойности на елементите. 40 | 41 | Пример за сортиращ алгоритъм, който работи за време $\Theta(n)$, е **сортирането чрез броене (Counting Sort)**. Алгоритъмът работи, като първо обхожда входния масив и създава масив за броене (или хистограма), който проследява колко пъти се среща всяка стойност. След това този масив се използва, за да се изчислят позициите, на които елементите трябва да бъдат поставени в сортирания масив. Накрая, елементите се преместват на правилните им позиции в новия изходен масив, което води до сортиран резултат. Този метод е особено ефективен, когато диапазонът от възможни стойности е сравнително малък спрямо размера на входния масив. 42 | 43 | ||Counting sort| 44 | |--|--| 45 | |Най-добър случай (BC) |$\Theta(n + m)$ | 46 | |Среден случай (AC) |$\Theta(n + m)$ | 47 | |Най-лош случай (WC) |$\Theta(n + m)$ | 48 | |Памет |$\Theta(n + m)$ | 49 | |Стабилност |:heavy_check_mark: | 50 | 51 | -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/images/MergeSort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_02/Sorting algorithms (Pt. 2)/images/MergeSort.png -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/images/MergeSortComplexity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_02/Sorting algorithms (Pt. 2)/images/MergeSortComplexity.png -------------------------------------------------------------------------------- /Sem_02/Sorting algorithms (Pt. 2)/images/QuickSort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_02/Sorting algorithms (Pt. 2)/images/QuickSort.png -------------------------------------------------------------------------------- /Sem_02/images/Task02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_02/images/Task02.png -------------------------------------------------------------------------------- /Sem_02/images/Task03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_02/images/Task03.png -------------------------------------------------------------------------------- /Sem_03/GenerateBoolVectors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void print(const std::vector& v) 5 | { 6 | std::cout << "["; 7 | for (auto it = v.begin(); it != v.end(); it++) 8 | { 9 | std::cout << (*it) << " "; 10 | } 11 | std::cout << "]" << std::endl; 12 | } 13 | 14 | void next(std::vector& v) 15 | { 16 | int ind = v.size() - 1; 17 | while (ind >= 0 && v[ind] == 1) 18 | { 19 | v[ind--] = 0; 20 | } 21 | if (ind >= 0) 22 | { 23 | v[ind] = 1; 24 | } 25 | } 26 | 27 | void nextWithIterators(std::vector& v) 28 | { 29 | auto it = v.rbegin(); // reverse iterator 30 | while (it != v.rend() && *it == 1) 31 | { 32 | *it++ = 0; 33 | } 34 | if (it != v.rend()) 35 | { 36 | *it = 1; 37 | } 38 | } 39 | 40 | void generateBoolVectors(int n) 41 | { 42 | std::vector v(n, 0); 43 | unsigned count = 1 << n; 44 | 45 | for (unsigned i = 0; i < count; i++) 46 | { 47 | print(v); 48 | nextWithIterators(v); 49 | } 50 | } 51 | 52 | int main() 53 | { 54 | generateBoolVectors(3); 55 | } 56 | -------------------------------------------------------------------------------- /Sem_03/README.md: -------------------------------------------------------------------------------- 1 | # Динамичен масив (вектор) 2 | 3 | ### Сложност на операциите му: 4 | - Добавяне на елемент на произволна позиция -> **$\Theta(n)$**. 5 | В частност, добавяне на елемент в края -> **$\Theta(1)$ амортизирано**. 6 | - Премахване на елемент от произволна позиция -> **$\Theta(n)$**. 7 | В частност, премахване на елемент от края -> **$\Theta(1)$**. 8 | - "Преглед" на произволен елемент -> **$\Theta(1)$**. 9 | 10 | Въпреки че операциите insert и erase имат теоретично "неблагоприятна" сложност, на практика те работят бързо за малки и средни вектори. Това се дължи на последователното разположение на елементите в паметта (виж [data locality](http://gameprogrammingpatterns.com/data-locality.html)). 11 | -------------------------------------------------------------------------------- /Sem_03/Vector/iterator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | class const_vector_iterator 6 | { 7 | public: 8 | const_vector_iterator(T* passedVal) : memPointer{passedVal} {} 9 | const_vector_iterator(T* passedVal, size_t push) : memPointer{passedVal + push} {} 10 | 11 | const_vector_iterator operator+(int offset) const 12 | { 13 | return memPointer + offset; 14 | } 15 | 16 | const_vector_iterator operator-(int offset) const 17 | { 18 | return memPointer - offset; 19 | } 20 | 21 | int operator-(const_vector_iterator other) const 22 | { 23 | return memPointer - other.memPointer; 24 | } 25 | 26 | const T* operator->() const noexcept 27 | { 28 | return memPointer; 29 | } 30 | 31 | const T& operator*() const noexcept 32 | { 33 | return *memPointer; 34 | } 35 | 36 | const T& operator[](int index) const 37 | { 38 | return *(memPointer + index); 39 | } 40 | 41 | template 42 | bool operator==(const const_vector_iterator

& other) const 43 | { 44 | return memPointer == other.memPointer; 45 | } 46 | 47 | template 48 | bool operator!=(const const_vector_iterator

& other) const 49 | { 50 | return !(memPointer == other.memPointer); 51 | } 52 | 53 | private: 54 | T* memPointer; 55 | }; 56 | 57 | template 58 | class vector_iterator 59 | { 60 | public: 61 | vector_iterator(T* passedVal) : memPointer{passedVal} {}; 62 | vector_iterator(T* passedVal, size_t push) : memPointer{passedVal + push} {}; 63 | 64 | vector_iterator& operator++() 65 | { 66 | memPointer++; 67 | return *this; 68 | } 69 | 70 | vector_iterator operator++(int) 71 | { 72 | vector_iterator it = *this; 73 | ++(*this); 74 | return it; 75 | } 76 | 77 | vector_iterator& operator--() 78 | { 79 | memPointer--; 80 | return *this; 81 | } 82 | 83 | vector_iterator operator--(int) 84 | { 85 | vector_iterator it = *this; 86 | --(*this); 87 | return it; 88 | } 89 | 90 | vector_iterator& operator+=(int offset) 91 | { 92 | memPointer += offset; 93 | return *this; 94 | } 95 | 96 | vector_iterator& operator-=(int offset) 97 | { 98 | memPointer -= offset; 99 | return *this; 100 | } 101 | 102 | vector_iterator operator+(int offset) const 103 | { 104 | return memPointer + offset; 105 | } 106 | 107 | vector_iterator operator-(int offset) const 108 | { 109 | return memPointer - offset; 110 | } 111 | 112 | T* operator->() 113 | { 114 | return memPointer; 115 | } 116 | 117 | const T* operator->() const 118 | { 119 | return memPointer; 120 | } 121 | 122 | T& operator*() 123 | { 124 | return *memPointer; 125 | } 126 | 127 | const T& operator*() const 128 | { 129 | return *memPointer; 130 | } 131 | 132 | operator const_vector_iterator() const 133 | { 134 | return const_vector_iterator(memPointer); 135 | } 136 | 137 | T& operator[](int index) 138 | { 139 | return *(memPointer + index); 140 | } 141 | 142 | const T& operator[](int index) const 143 | { 144 | return *(memPointer + index); 145 | } 146 | 147 | template 148 | bool operator==(const vector_iterator

& other) const 149 | { 150 | return memPointer == other.memPointer; 151 | } 152 | 153 | template 154 | bool operator!=(const vector_iterator

& other) const 155 | { 156 | return !(memPointer == other.memPointer); 157 | } 158 | 159 | private: 160 | T* memPointer; 161 | }; 162 | 163 | template 164 | class reverse_vector_iterator 165 | { 166 | public: 167 | reverse_vector_iterator(T* passedVal) : memPointer{passedVal} {}; 168 | reverse_vector_iterator(T* passedVal, size_t push) : memPointer{passedVal + push} {}; 169 | 170 | reverse_vector_iterator& operator++() 171 | { 172 | memPointer--; 173 | return *this; 174 | } 175 | 176 | reverse_vector_iterator operator++(int) 177 | { 178 | reverse_vector_iterator it = *this; 179 | --(*this); 180 | return it; 181 | } 182 | 183 | reverse_vector_iterator& operator--() 184 | { 185 | memPointer++; 186 | return *this; 187 | } 188 | 189 | reverse_vector_iterator operator--(int) 190 | { 191 | reverse_vector_iterator it = *this; 192 | ++(*this); 193 | return it; 194 | } 195 | 196 | reverse_vector_iterator& operator+=(int offset) 197 | { 198 | memPointer -= offset; 199 | return *this; 200 | } 201 | 202 | reverse_vector_iterator& operator-=(int offset) 203 | { 204 | memPointer += offset; 205 | return *this; 206 | } 207 | 208 | reverse_vector_iterator operator+(int offset) const 209 | { 210 | return memPointer - offset; 211 | } 212 | 213 | reverse_vector_iterator operator-(int offset) const 214 | { 215 | return memPointer + offset; 216 | } 217 | 218 | T* operator->() 219 | { 220 | return memPointer; 221 | } 222 | 223 | const T* operator->() const 224 | { 225 | return memPointer; 226 | } 227 | 228 | T& operator*() 229 | { 230 | return *memPointer; 231 | } 232 | 233 | const T& operator*() const 234 | { 235 | return *memPointer; 236 | } 237 | 238 | T& operator[](int index) 239 | { 240 | return *(memPointer - index); 241 | } 242 | 243 | const T& operator[](int index) const 244 | { 245 | return *(memPointer - index); 246 | } 247 | 248 | template 249 | bool operator==(const reverse_vector_iterator

& other) const 250 | { 251 | return memPointer == other.memPointer; 252 | } 253 | 254 | template 255 | bool operator!=(const reverse_vector_iterator

& other) const 256 | { 257 | return !(memPointer == other.memPointer); 258 | } 259 | 260 | private: 261 | T* memPointer; 262 | }; 263 | -------------------------------------------------------------------------------- /Sem_04/MergeSort_SLL.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | Node* next = nullptr; 8 | 9 | Node(const T& data, Node* next = nullptr) : data(data), next(next) {} 10 | }; 11 | 12 | template 13 | void printList(Node* iter) 14 | { 15 | while (iter) 16 | { 17 | std::cout << iter->data; 18 | if (iter->next) 19 | { 20 | std::cout << " -> "; 21 | } 22 | iter = iter->next; 23 | } 24 | } 25 | 26 | template 27 | void freeList(Node* iter) 28 | { 29 | while (iter) 30 | { 31 | Node* toDelete = iter; 32 | iter = iter->next; 33 | delete toDelete; 34 | } 35 | } 36 | 37 | template 38 | void push_back(Node*& begin, Node*& end, Node* toAdd) 39 | { 40 | if (begin == nullptr) 41 | { 42 | begin = end = toAdd; 43 | } 44 | else 45 | { 46 | end->next = toAdd; 47 | end = toAdd; 48 | } 49 | } 50 | 51 | template 52 | Node* mergeLists(Node* first, Node* second) 53 | { 54 | Node* resultBegin = nullptr, *resultEnd = nullptr; 55 | while (first && second) 56 | { 57 | if (first->data < second->data) 58 | { 59 | push_back(resultBegin, resultEnd, first); 60 | first = first->next; 61 | } 62 | else 63 | { 64 | push_back(resultBegin, resultEnd, second); 65 | second = second->next; 66 | } 67 | } 68 | if (!first) 69 | { 70 | push_back(resultBegin, resultEnd, second); 71 | } 72 | else 73 | { 74 | push_back(resultBegin, resultEnd, first); 75 | } 76 | return resultBegin; 77 | } 78 | 79 | template 80 | Node* getMid(Node* iter) 81 | { 82 | Node* slow = iter; 83 | Node* fast = iter->next; 84 | 85 | while (fast && fast->next) 86 | { 87 | slow = slow->next; 88 | fast = fast->next->next; 89 | } 90 | return slow; 91 | } 92 | 93 | template 94 | Node* mergeSort(Node* list) 95 | { 96 | if (!list || !list->next) 97 | { 98 | return list; 99 | } 100 | 101 | Node* mid = getMid(list); 102 | Node* left = list; 103 | Node* right = mid->next; 104 | mid->next = nullptr; 105 | 106 | left = mergeSort(left); 107 | right = mergeSort(right); 108 | 109 | return mergeLists(left, right); 110 | } 111 | 112 | int main() 113 | { 114 | Node* list = new Node{ 23, new Node{5, new Node{22, new Node{5}}} }; 115 | 116 | list = mergeSort(list); 117 | printList(list); 118 | freeList(list); 119 | list = nullptr; 120 | } 121 | -------------------------------------------------------------------------------- /Sem_04/QuickSort_SLL.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | Node* next = nullptr; 9 | 10 | Node(const T& data, Node* next = nullptr) : data(data), next(next) {} 11 | }; 12 | 13 | template 14 | void printList(Node* iter) 15 | { 16 | while (iter) 17 | { 18 | std::cout << iter->data; 19 | if (iter->next) 20 | { 21 | std::cout << " -> "; 22 | } 23 | iter = iter->next; 24 | } 25 | } 26 | 27 | template 28 | void freeList(Node* iter) 29 | { 30 | while (iter) 31 | { 32 | Node* toDelete = iter; 33 | iter = iter->next; 34 | delete toDelete; 35 | } 36 | } 37 | 38 | template 39 | void push_back(Node*& begin, Node*& end, Node* toAdd) 40 | { 41 | if (begin == nullptr) 42 | { 43 | begin = end = toAdd; 44 | } 45 | else 46 | { 47 | end->next = toAdd; 48 | end = toAdd; 49 | } 50 | } 51 | 52 | template 53 | std::pair*, Node*> partition(Node* current, const PredicateType& pred) 54 | { 55 | Node* trueListBegin = nullptr; 56 | Node* trueListEnd = nullptr; 57 | 58 | Node* falseListBegin = nullptr; 59 | Node* falseListEnd = nullptr; 60 | 61 | while (current != nullptr) 62 | { 63 | if (pred(current->data)) 64 | { 65 | push_back(trueListBegin, trueListEnd, current); 66 | } 67 | else 68 | { 69 | push_back(falseListBegin, falseListEnd, current); 70 | } 71 | current = current->next; 72 | } 73 | if (trueListEnd) 74 | { 75 | trueListEnd->next = nullptr; 76 | } 77 | if (falseListEnd) 78 | { 79 | falseListEnd->next = nullptr; 80 | } 81 | 82 | return std::make_pair(trueListBegin, falseListBegin); 83 | } 84 | 85 | template 86 | std::pair*, Node*> concatLists(Node* leftBegin, Node* leftEnd, Node* rightBegin, Node* rightEnd) 87 | { 88 | if (leftBegin == nullptr) 89 | { 90 | return std::make_pair(rightBegin, rightEnd); 91 | } 92 | if (rightBegin == nullptr) 93 | { 94 | return std::make_pair(leftBegin, leftEnd); 95 | } 96 | 97 | leftEnd->next = rightBegin; 98 | return std::make_pair(leftBegin, rightEnd); 99 | } 100 | 101 | template 102 | std::pair*, Node*> quickSort(Node* list) 103 | { 104 | if (!list || !list->next) 105 | { 106 | return std::make_pair(list, list); 107 | } 108 | 109 | T pivot = list->data; 110 | std::pair*, Node*> partitionResults = partition(list, [pivot](int el) {return el < pivot; }); 111 | 112 | Node* pivotPtr = partitionResults.second; 113 | 114 | std::pair*, Node*> leftToConcat = quickSort(partitionResults.first); 115 | std::pair*, Node*> rightToConcat = quickSort(partitionResults.second->next); // skip the pivot 116 | 117 | pivotPtr->next = rightToConcat.first; // attach the pivot to the beginning of right 118 | rightToConcat.first = pivotPtr; 119 | if (!rightToConcat.second) // the right was empty list, we should concat only the pivot 120 | { 121 | rightToConcat.second = pivotPtr; 122 | } 123 | 124 | return concatLists(leftToConcat.first, leftToConcat.second, rightToConcat.first, rightToConcat.second); 125 | } 126 | 127 | int main() 128 | { 129 | Node* list = new Node{ 23, new Node{5, new Node{22, new Node{5}}} }; 130 | 131 | std::pair*, Node*> res = quickSort(list); 132 | list = res.first; 133 | printList(list); 134 | freeList(list); 135 | list = nullptr; 136 | } 137 | -------------------------------------------------------------------------------- /Sem_04/README.md: -------------------------------------------------------------------------------- 1 | # Свързан списък 2 | 3 | Структурата данни *"Свързан списък"* представлява поредица от елементи, като всеки от тях съдържа не само информацията, която бихме пазили в масив, ами и информация кой е *следващият (и предишният)* елемент в поредицата. 4 | В най-стандартната имплементация, тази информация представлява указател към следващия (и предишния) елемент. Така лесно можем да обходим всички елементи от структурата - започвайки от първия, следваме неговия указател към следващия, от там към по-следващия и т.н. 5 | Допълнително ще пазим два указателя - към първия и към последния елемент на списъка. Това ще ни позволява лесно да добавяме елемент както в началото, така и в края на списъка. 6 | 7 | ## Едносвързан списък (Singly Linked List) 8 | 9 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_04/images/SLL.png) 10 | 11 | ### Сложност на операциите му: 12 | - Добавяне на елемент в началото -> **$\Theta(1)$**. 13 | - Добавяне на елемент в края -> **$\Theta(1)$**. 14 | - Премахване на елемент от началото -> **$\Theta(1)$**. 15 | - Добавяне на елемент **след** произволна позиция, към която имаме итератор -> **$\Theta(1)$**. 16 | - Премахване на елемент **след** произволна позиция, към която имаме итератор -> **$\Theta(1)$**. 17 | - Всичи останали добавяния/премахвания -> **$\Theta(n)$**. 18 | 19 | ## Двусвързан списък (Doubly Linked List) 20 | 21 | ### Сложност на операциите му: 22 | - Добавяне на елемент в началото -> **$\Theta(1)$**. 23 | - Добавяне на елемент в края -> **$\Theta(1)$**. 24 | - Премахване на елемент от началото -> **$\Theta(1)$**. 25 | - Премахване на елемент от края -> **$\Theta(1)$**. 26 | - Добавяне на елемент на произволна позиция, към която имаме итератор -> **$\Theta(1)$**. 27 | - Премахване на елемент от произволна позиция, към която имаме итератор -> **$\Theta(1)$**. 28 | 29 | --- 30 | 31 | ## Задачи 32 | 33 | ### Задача 1: Quick sort на списък. 34 | 35 | ### Задача 2: Merge sort на списък. 36 | 37 | ### Задача 3: Flatten на списък. 38 | 39 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_04/images/Multilevel-list.png) 40 | 41 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_04/images/One-level-list.png) 42 | 43 | ### Задача 4: 44 | Даден е едносвързан **цикличен** списък, чиито елементи са стрингове. 45 | Да се реализира функция *unite(Node\* list)*, която получава като параметър указател към елемент на цикличния списък от стрингове и обединява всички двойки последователни елементи на списъка, за които последния символ на единия елемент съвпада с първия символ на непосредствено следващия му елемент, в общ елемент, чийто низ е съставен от слепването на стринговете на двата елемента, разделени с тире. 46 | Функцията да изкарва списъка на стандартния изход, започвайки от лексикографски най-малкия низ. 47 | 48 | Вход: 49 | ```c++ 50 | street -> taxi -> ink -> dog -> smile -> eat -> tall -> pass 51 | ``` 52 | Изход: 53 | ```c++ 54 | dog 55 | 56 | smile -> eat -> tall 57 | 58 | pass -> street -> taxi -> ink 59 | ``` 60 | 61 | ### Задача 5: 62 | Даден е списък **l** с елементи списъци от естествени числа в интервала [0,..,9]. От всеки списък $l_i$ получаваме две числа по следния начин: 63 | - Обхождайки го отпред назад, получваме число $n_i$. 64 | - Обхождайки го отзад напред, получаваме число $n_{i_{reversed}}$. 65 | 66 | Да се дефинира функция, която намира сумата $n_i$ + $n_{i_{reversed}}$ за 0 <= i <= l.size() - 1. 67 | 68 | *Вход:*                                              *Изход:* 69 | ```c++ 70 | 1 -> 2 809 71 | | (12 + 21 + 234 + 432 + 55 + 55) 72 | v 73 | 2 -> 3 -> 4 74 | | 75 | v 76 | empty 77 | | 78 | v 79 | 5 -> 5 80 | ``` 81 | -------------------------------------------------------------------------------- /Sem_04/Solutions/Task03.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | Node* prev; 8 | Node* next; 9 | Node* child; 10 | 11 | Node(const T& data) : data(data), prev(nullptr), next(nullptr), child(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* iter) 16 | { 17 | while (iter) 18 | { 19 | Node* toDelete = iter; 20 | iter = iter->next; 21 | delete toDelete; 22 | } 23 | } 24 | 25 | template 26 | void print(Node* iter) 27 | { 28 | while (iter) 29 | { 30 | std::cout << iter->data; 31 | if (iter->next) 32 | { 33 | std::cout << "->"; 34 | } 35 | iter = iter->next; 36 | } 37 | std::cout << std::endl; 38 | } 39 | 40 | template 41 | void flatten(Node* list) 42 | { 43 | Node* iter = list; 44 | 45 | while (iter) 46 | { 47 | if (iter->child) 48 | { 49 | Node* tempNext = iter->next; 50 | Node* tempChildIter = iter->child; 51 | 52 | iter->next = tempChildIter; 53 | tempChildIter->prev = iter; 54 | iter->child = nullptr; 55 | 56 | while (tempChildIter->next) 57 | { 58 | tempChildIter = tempChildIter->next; 59 | } 60 | 61 | if (tempNext) 62 | { 63 | tempChildIter->next = tempNext; 64 | tempNext->prev = tempChildIter; 65 | } 66 | } 67 | 68 | iter = iter->next; 69 | } 70 | 71 | print(list); 72 | free(list); 73 | } 74 | 75 | int main() 76 | { 77 | Node* n1 = new Node(1); 78 | Node* n2 = new Node(2); 79 | Node* n3 = new Node(3); 80 | Node* n4 = new Node(4); 81 | Node* n7 = new Node(7); 82 | Node* n8 = new Node(8); 83 | Node* n9 = new Node(9); 84 | Node* n10 = new Node(10); 85 | Node* n11 = new Node(11); 86 | Node* n12 = new Node(12); 87 | 88 | n1->next = n2; 89 | n2->prev = n1; 90 | n2->next = n3; 91 | n3->prev = n2; 92 | n3->next = n4; 93 | n4->prev = n3; 94 | 95 | n3->child = n7; 96 | n7->next = n8; 97 | n8->prev = n7; 98 | n8->next = n9; 99 | n9->prev = n8; 100 | n9->next = n10; 101 | n10->prev = n9; 102 | 103 | n8->child = n11; 104 | n11->next = n12; 105 | n12->prev = n11; 106 | 107 | flatten(n1); 108 | } 109 | -------------------------------------------------------------------------------- /Sem_04/Solutions/Task04.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Node 5 | { 6 | std::string data; 7 | Node* next; 8 | 9 | Node(const std::string& data, Node* next = nullptr) : data(data), next(next) {} 10 | }; 11 | 12 | void free(Node* iter) 13 | { 14 | if (!iter) 15 | { 16 | return; 17 | } 18 | 19 | Node* first = iter; 20 | do 21 | { 22 | Node* toDelete = iter; 23 | iter = iter->next; 24 | delete toDelete; 25 | } 26 | while (iter != first); 27 | } 28 | 29 | void print(Node* iter) 30 | { 31 | if (!iter) 32 | { 33 | return; 34 | } 35 | 36 | Node* first = iter; 37 | do 38 | { 39 | std::cout << iter->data << std::endl; 40 | iter = iter->next; 41 | } 42 | while (iter != first); 43 | } 44 | 45 | bool shouldUnite(const std::string& lhs, const std::string& rhs) 46 | { 47 | if (lhs.empty() || rhs.empty()) 48 | { 49 | return false; 50 | } 51 | return lhs[lhs.size() - 1] == rhs[0]; 52 | } 53 | 54 | void absorb(Node* lhs, Node* rhs) 55 | { 56 | lhs->data.append(" -> "); 57 | lhs->data.append(rhs->data); 58 | 59 | lhs->next = rhs->next; 60 | delete rhs; 61 | } 62 | 63 | void unite(Node* list) 64 | { 65 | if (!list) 66 | { 67 | return; 68 | } 69 | 70 | Node* first = list; 71 | Node*& lexMin = first; 72 | 73 | bool shouldFinish = false; 74 | 75 | do 76 | { 77 | if (list->next == first) 78 | { 79 | shouldFinish = true; 80 | } 81 | 82 | if (list != list->next && shouldUnite(list->data, list->next->data)) 83 | { 84 | if (list->next == first) // If absorbing the first node 85 | { 86 | first = list; 87 | } 88 | absorb(list, list->next); 89 | } 90 | else 91 | { 92 | list = list->next; 93 | if (list->data < lexMin->data) 94 | { 95 | lexMin = list; 96 | } 97 | } 98 | } 99 | while (!shouldFinish); 100 | 101 | print(lexMin); 102 | free(lexMin); 103 | } 104 | 105 | int main() 106 | { 107 | Node* f1 = new Node("street"); 108 | Node* f2 = new Node("taxi"); 109 | Node* f3 = new Node("ink"); 110 | Node* f4 = new Node("dog"); 111 | Node* f5 = new Node("smile"); 112 | Node* f6 = new Node("eat"); 113 | Node* f7 = new Node("tall"); 114 | Node* f8 = new Node("pass"); 115 | 116 | f1->next = f2; 117 | f2->next = f3; 118 | f3->next = f4; 119 | f4->next = f5; 120 | f5->next = f6; 121 | f6->next = f7; 122 | f7->next = f8; 123 | f8->next = f1; 124 | 125 | unite(f6); 126 | } 127 | -------------------------------------------------------------------------------- /Sem_04/Solutions/Task05.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int getNumberFromList(const std::list& l) 5 | { 6 | int number = 0; 7 | 8 | for (std::list::const_iterator it = l.begin(); it != l.end(); ++it) 9 | { 10 | (number *= 10) += (*it); 11 | } 12 | 13 | return number; 14 | } 15 | 16 | int getReversedNumberFromList(const std::list& l) 17 | { 18 | int number = 0; 19 | 20 | for (std::list::const_reverse_iterator it = l.rbegin(); it != l.rend(); ++it) 21 | { 22 | (number *= 10) += (*it); 23 | } 24 | 25 | return number; 26 | } 27 | 28 | int sum(const std::list>& l) 29 | { 30 | int sum = 0; 31 | 32 | for (auto it = l.begin(); it != l.end(); ++it) 33 | { 34 | sum += (getNumberFromList(*it) + getReversedNumberFromList(*it)); 35 | } 36 | 37 | return sum; 38 | } 39 | 40 | int main() 41 | { 42 | std::list> l; 43 | 44 | std::list l1; 45 | l1.push_back(1); 46 | l1.push_back(2); 47 | l1.push_back(3); 48 | std::list l2; 49 | l2.push_back(4); 50 | l2.push_back(5); 51 | l2.push_back(6); 52 | std::list l3; 53 | std::list l4; 54 | l4.push_back(7); 55 | l4.push_back(8); 56 | l4.push_back(9); 57 | std::list l5; 58 | l5.push_back(10); 59 | 60 | l.push_back(l1); 61 | l.push_back(l2); 62 | l.push_back(l3); 63 | l.push_back(l4); 64 | l.push_back(l5); 65 | 66 | std::cout << sum(l); 67 | } 68 | -------------------------------------------------------------------------------- /Sem_04/images/Multilevel-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_04/images/Multilevel-list.png -------------------------------------------------------------------------------- /Sem_04/images/One-level-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_04/images/One-level-list.png -------------------------------------------------------------------------------- /Sem_04/images/SLL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_04/images/SLL.png -------------------------------------------------------------------------------- /Sem_05/Queue/LinkedQueue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | class queue 6 | { 7 | struct Node 8 | { 9 | Node(const T& data) : data(data), next(nullptr) {} 10 | Node(T&& data) : data(std::move(data)), next(nullptr) {} 11 | 12 | T data; 13 | Node* next; 14 | }; 15 | 16 | public: 17 | queue() = default; 18 | 19 | queue(const queue& other); 20 | queue& operator=(const queue& other); 21 | 22 | queue(queue&& other) noexcept; 23 | queue& operator=(queue&& other) noexcept; 24 | 25 | ~queue() noexcept; 26 | 27 | void push(const T& el); 28 | void push(T&& el); 29 | 30 | template 31 | T emplace(Args&&... args); 32 | 33 | void pop(); 34 | 35 | T& front(); 36 | const T& front() const; 37 | 38 | T& back(); 39 | const T& back() const; 40 | 41 | size_t getSize() const; 42 | bool empty() const; 43 | 44 | private: 45 | Node* head = nullptr; 46 | Node* tail = nullptr; 47 | size_t size = 0; 48 | 49 | void copyFrom(const queue& other); 50 | void moveFrom(queue&& other); 51 | void free(); 52 | }; 53 | 54 | template 55 | void queue::push(const T& el) 56 | { 57 | Node* newNode = new Node(el); 58 | if (empty()) 59 | { 60 | head = tail = newNode; 61 | } 62 | else 63 | { 64 | tail->next = newNode; 65 | tail = newNode; 66 | } 67 | size++; 68 | } 69 | 70 | template 71 | void queue::push(T&& el) 72 | { 73 | Node* newNode = new Node(std::move(el)); 74 | if (empty()) 75 | { 76 | head = tail = newNode; 77 | } 78 | else 79 | { 80 | tail->next = newNode; 81 | tail = newNode; 82 | } 83 | size++; 84 | } 85 | 86 | template 87 | template 88 | T queue::emplace(Args&&... args) 89 | { 90 | T newObj(std::forward(args)...); 91 | Node* newNode = new Node(newObj); 92 | if (empty()) 93 | { 94 | head = tail = newNode; 95 | } 96 | else 97 | { 98 | tail->next = newNode; 99 | tail = newNode; 100 | } 101 | size++; 102 | return newObj; 103 | } 104 | 105 | template 106 | void queue::pop() 107 | { 108 | if (!head) 109 | { 110 | throw std::logic_error("Empty queue\n"); 111 | } 112 | 113 | if (head == tail) 114 | { 115 | delete head; 116 | head = tail = nullptr; 117 | } 118 | else 119 | { 120 | Node* toDelete = head; 121 | head = head->next; 122 | delete toDelete; 123 | } 124 | size--; 125 | } 126 | 127 | template 128 | T& queue::front() 129 | { 130 | if (!head) 131 | { 132 | throw std::logic_error("Empty queue\n"); 133 | } 134 | return head->data; 135 | } 136 | 137 | template 138 | const T& queue::front() const 139 | { 140 | if (!head) 141 | { 142 | throw std::logic_error("Empty queue\n"); 143 | } 144 | return head->data; 145 | } 146 | 147 | template 148 | T& queue::back() 149 | { 150 | if (!head) 151 | { 152 | throw std::logic_error("Empty queue\n"); 153 | } 154 | return tail->data; 155 | } 156 | 157 | template 158 | const T& queue::back() const 159 | { 160 | if (!head) 161 | { 162 | throw std::logic_error("Empty queue\n"); 163 | } 164 | return tail->data; 165 | } 166 | 167 | template 168 | size_t queue::getSize() const 169 | { 170 | return size; 171 | } 172 | 173 | template 174 | bool queue::empty() const 175 | { 176 | return size == 0; 177 | } 178 | 179 | template 180 | queue::queue(const queue& other) 181 | { 182 | copyFrom(other); 183 | } 184 | 185 | template 186 | queue& queue::operator=(const queue& other) 187 | { 188 | if (this != &other) 189 | { 190 | free(); 191 | copyFrom(other); 192 | } 193 | return *this; 194 | } 195 | 196 | template 197 | queue::queue(queue&& other) noexcept 198 | { 199 | moveFrom(std::move(other)); 200 | } 201 | 202 | template 203 | queue& queue::operator=(queue&& other) noexcept 204 | { 205 | if (this != &other) 206 | { 207 | free(); 208 | moveFrom(std::move(other)); 209 | } 210 | return *this; 211 | } 212 | 213 | template 214 | queue::~queue() noexcept 215 | { 216 | free(); 217 | } 218 | 219 | template 220 | void queue::copyFrom(const queue& other) 221 | { 222 | Node* iter = other.head; 223 | while (iter) 224 | { 225 | push_back(iter->data); 226 | iter = iter->next; 227 | } 228 | } 229 | 230 | template 231 | void queue::moveFrom(queue&& other) 232 | { 233 | head = other.head; 234 | tail = other.tail; 235 | size = other.size; 236 | other.head = other.tail = nullptr; 237 | other.size = 0; 238 | } 239 | 240 | template 241 | void queue::free() 242 | { 243 | Node* iter = head; 244 | while (iter) 245 | { 246 | Node* toDelete = iter; 247 | iter = iter->next; 248 | delete toDelete; 249 | } 250 | head = tail = nullptr; 251 | size = 0; 252 | } 253 | -------------------------------------------------------------------------------- /Sem_05/Queue/QueueWithTemplateContainer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "deque.hpp" 3 | 4 | template > 5 | class queue 6 | { 7 | public: 8 | void push(const T& el) 9 | { 10 | c.push_back(el); 11 | } 12 | void pop() 13 | { 14 | c.pop_front(); 15 | } 16 | 17 | T& front() 18 | { 19 | return c.front(); 20 | } 21 | const T& front() const 22 | { 23 | return c.front(); 24 | } 25 | 26 | T& back() 27 | { 28 | return c.back(); 29 | } 30 | const T& back() const 31 | { 32 | return c.back(); 33 | } 34 | 35 | bool empty() const 36 | { 37 | return c.empty(); 38 | } 39 | size_t size() const 40 | { 41 | return c.size(); 42 | } 43 | 44 | private: 45 | Container c; 46 | }; 47 | -------------------------------------------------------------------------------- /Sem_05/README.md: -------------------------------------------------------------------------------- 1 | ## Задачи 2 | 3 | ### Задача 1 4 | Даден е списък **l** с n елемента. 5 | Да се дефинира функция *shuffle()*, която получава адреса на първия елемент на списъка. Функцията да пренарежда възлите на списъка така, че *елементите от втората половина на списъка да се преместят в началото на списъка, но в обратен ред* (при списъци с нечетен брой елементи считаме средния елемент за принадлежащ към първата половина на списъка). 6 | 7 | *Пример:* 8 | *L1 → L2 → L3 → L4 → L5 се преобразува до L5 → L4 → L1 → L2 → L3* 9 | 10 | ### Задача 2 11 | Даден е списък **l** с елементи стекове. 12 | Да се дефинира функция *equalize(l)*, която размества елементите на стековете така, че *да няма два стека в l с разлика в броя на елементите, по-голяма от 1*. 13 | 14 | *Пример:* 15 | *Вход:*                                              *Изход:* 16 | ```c++ 17 | 5 -> 4 -> 3 -> 2 -> 1 5 -> 4 -> 3 18 | | | 19 | v v 20 | 7 -> 6 7 -> 6 -> 1 21 | | | 22 | v v 23 | 11 -> 10 -> 9 -> 8 11 -> 10 -> 9 24 | | | 25 | v v 26 | 12 12 -> 2 -> 8 27 | ``` 28 | 29 | ### Задача 3 30 | Даден е списък **l** с елементи опашки от естествени числа. 31 | Да се дефинира функция, която връща *дали последователната конкатенация на всички опашки образува сортирана редица.* 32 | **Да се реши с константна памет и да не се променят входните данни!** 33 | 34 | *Вход:*                                              *Изход:* 35 | ```c++ 36 | 1 -> 2 -> 3 yes 37 | | 1 -> 2 -> 3 -> 6 -> 9 -> 10 -> 38 | v 11 -> 22 -> 33 39 | 6 -> 9 -> 10 40 | | 41 | v 42 | 11 -> 22 -> 33 43 | | 44 | v 45 | empty 46 | ``` 47 | *Вход:*                                              *Изход:* 48 | ```c++ 49 | 1 no 50 | | 51 | v 52 | 6 -> 9 -> 10 53 | | 54 | v 55 | 9 -> 22 56 | ``` 57 | *Вход:*                                              *Изход:* 58 | ```c++ 59 | 1 -> 2 -> 3 no 60 | | 61 | v 62 | 4 -> 5 -> 4 63 | ``` 64 | 65 | ### Задача 4 66 | Даден е списък **l** с елементи опашки от естествени числа. 67 | - Ако **n** последователни опашки са с равна големина, то те са "подобни" и *трябва да се конкатенират*. 68 | - Ако **две** последователни опашки не са с равен брой елементи, *то той трябва да се изравни с разлика до единица*. 69 | 70 | *Вход:*                                              *Изход:* 71 | ```c++ 72 | 2 -> 4 -> 6 2 -> 4 -> 6 -> 8 -> 0 -> 7 73 | | | 74 | v v 75 | 8 -> 0 -> 7 7 -> 9 -> 23 -> 22 76 | | | 77 | v v 78 | 5 -> 7 -> 9 -> 23 -> 22 9 -> 1 -> 5 79 | | | 80 | v v 81 | 9 -> 1 2 -> 1 -> 33 -> 44 82 | | 83 | V 84 | 2 85 | | 86 | v 87 | 1 88 | | 89 | v 90 | 33 -> 44 91 | ``` 92 | -------------------------------------------------------------------------------- /Sem_05/Solutions/Task01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | Node* next; 9 | }; 10 | 11 | template 12 | int getNumOfNodes(Node* list) 13 | { 14 | Node* iter = list; 15 | int countOfNodes = 0; 16 | 17 | while (iter != nullptr) 18 | { 19 | countOfNodes++; 20 | iter = iter->next; 21 | } 22 | 23 | return countOfNodes; 24 | } 25 | 26 | template 27 | Node* getMiddle(Node* list) 28 | { 29 | Node* slowIter = list; 30 | Node* fastIter = list; 31 | 32 | while (fastIter != nullptr && fastIter->next != nullptr) 33 | { 34 | slowIter = slowIter->next; 35 | fastIter = fastIter->next->next; 36 | } 37 | 38 | if (getNumOfNodes(list) % 2 == 0) 39 | { 40 | return slowIter; 41 | } 42 | else 43 | { 44 | return slowIter->next; 45 | } 46 | } 47 | 48 | template 49 | void shuffle(Node* start) 50 | { 51 | Node* middleNode = getMiddle(start); 52 | Node* beginningOfFirstHalf = start; 53 | Node* endOfFirstHalf = start; 54 | 55 | Node* iter = start->next; 56 | 57 | std::stack*> nodesToReverse; 58 | 59 | while (iter != middleNode) 60 | { 61 | endOfFirstHalf->next = iter; 62 | endOfFirstHalf = endOfFirstHalf->next; 63 | 64 | iter = iter->next; 65 | } 66 | 67 | while (iter != nullptr) 68 | { 69 | nodesToReverse.push(iter); 70 | iter = iter->next; 71 | } 72 | 73 | Node* firstOfListToReturn = nodesToReverse.top(); 74 | Node* listToReturn = nodesToReverse.top(); 75 | 76 | nodesToReverse.pop(); 77 | 78 | while (!nodesToReverse.empty()) 79 | { 80 | listToReturn->next = nodesToReverse.top(); 81 | listToReturn = listToReturn->next; 82 | nodesToReverse.pop(); 83 | } 84 | 85 | listToReturn->next = beginningOfFirstHalf; 86 | endOfFirstHalf->next = nullptr; 87 | } 88 | 89 | int main() 90 | { 91 | Node* five = new Node; 92 | five->data = 5; 93 | five->next = nullptr; 94 | 95 | Node* four = new Node; 96 | four->data = 4; 97 | four->next = five; 98 | 99 | Node* three = new Node; 100 | three->data = 3; 101 | three->next = four; 102 | 103 | Node* two = new Node; 104 | two->data = 2; 105 | two->next = three; 106 | 107 | Node* one = new Node; 108 | one->next = two; 109 | one->data = 1; 110 | 111 | shuffle(one); 112 | 113 | while (five) 114 | { 115 | std::cout << five->data << " "; 116 | five = five->next; 117 | } 118 | 119 | delete one, two, three, four, five; 120 | } 121 | -------------------------------------------------------------------------------- /Sem_05/Solutions/Task02.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void equalize(std::list>& arg) 7 | { 8 | int numberOfStacks = arg.size(); 9 | int numberOfElements = 0; 10 | 11 | for (const std::stack& x : arg) 12 | { 13 | numberOfElements += x.size(); 14 | } 15 | 16 | int median = numberOfElements / numberOfStacks; 17 | bool eq = numberOfElements % numberOfStacks; 18 | 19 | std::vector> overMedian; 20 | std::vector> underMedian; 21 | 22 | for (const std::stack& x : arg) 23 | { 24 | if (x.size() < median) 25 | { 26 | underMedian.push_back(x); 27 | } 28 | else if (x.size() >= median) 29 | { 30 | overMedian.push_back(x); 31 | } 32 | } 33 | 34 | int iteratorOverMedian = 0; 35 | int iteratorUnderMedian = 0; 36 | 37 | for (std::stack& x : underMedian) 38 | { 39 | while (x.size() <= median - !(eq)) 40 | { 41 | while (overMedian[iteratorOverMedian].size() == median) 42 | { 43 | if (overMedian.size() == iteratorOverMedian + 1) 44 | { 45 | overMedian.push_back(underMedian[iteratorUnderMedian++]); 46 | } 47 | ++iteratorOverMedian; 48 | } 49 | x.push(overMedian[iteratorOverMedian].top()); 50 | overMedian[iteratorOverMedian].pop(); 51 | } 52 | } 53 | for (int i = 0; i < iteratorUnderMedian; i++) 54 | { 55 | overMedian.pop_back(); 56 | } 57 | 58 | arg.clear(); 59 | 60 | for (int i = 0; i < overMedian.size(); i++) 61 | { 62 | arg.push_back(overMedian[i]); 63 | } 64 | 65 | for (int i = 0; i < underMedian.size(); i++) 66 | { 67 | arg.push_back(underMedian[i]); 68 | } 69 | } 70 | 71 | int main() 72 | { 73 | std::list> l; 74 | std::stack a; 75 | std::stack d; 76 | std::stack e; 77 | std::stack c; 78 | std::stack b; 79 | 80 | for (int i = 0; i < 4; i++) 81 | { 82 | a.push(i); 83 | } 84 | 85 | for (int i = 0; i < 9; i++) 86 | { 87 | b.push(i); 88 | } 89 | 90 | for (int i = 0; i < 12; i++) 91 | { 92 | c.push(i); 93 | } 94 | 95 | for (int i = 0; i < 16; i++) 96 | { 97 | d.push(i); 98 | } 99 | 100 | for (int i = 0; i < 20; i++) 101 | { 102 | e.push(i); 103 | } 104 | 105 | l.push_back(a); 106 | l.push_back(b); 107 | l.push_back(c); 108 | l.push_back(d); 109 | l.push_back(e); 110 | 111 | equalize(l); 112 | 113 | for (std::stack a : l) 114 | { 115 | std::cout << a.size() << ": "; 116 | while (!a.empty()) 117 | { 118 | std::cout << a.top() << " "; 119 | a.pop(); 120 | } 121 | std::cout << std::endl; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Sem_05/Solutions/Task03.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | bool isQueueSorted(std::queue& q, int& lastEl) 7 | { 8 | if (q.empty()) 9 | { 10 | return true; 11 | } 12 | 13 | for (size_t i = 0; i < q.size(); i++) 14 | { 15 | int current = q.front(); 16 | q.pop(); 17 | 18 | if (i == q.size()) 19 | { 20 | lastEl = current; 21 | } 22 | 23 | if ((i != q.size()) && current > q.front()) 24 | { 25 | return false; 26 | } 27 | 28 | q.push(current); 29 | } 30 | 31 | return true; 32 | } 33 | 34 | bool isSortedSequence(std::list>& l) 35 | { 36 | int lastEl = INT_MIN; 37 | for (std::list>::iterator it = l.begin(); it != l.end(); ++it) 38 | { 39 | if (it != l.begin()) 40 | { 41 | if (!(*it).empty() && lastEl > (*it).front()) 42 | { 43 | return false; 44 | } 45 | } 46 | 47 | if (!isQueueSorted(*it, lastEl)) 48 | { 49 | return false; 50 | } 51 | } 52 | 53 | return true; 54 | } 55 | 56 | int main() 57 | { 58 | std::list> l; 59 | 60 | std::queue q1; 61 | q1.push(1); 62 | q1.push(2); 63 | q1.push(3); 64 | std::queue q2; 65 | q2.push(6); 66 | q2.push(9); 67 | q2.push(10); 68 | std::queue q3; 69 | q3.push(10); 70 | q3.push(22); 71 | q3.push(23); 72 | std::queue q4; 73 | 74 | l.push_back(q1); 75 | l.push_back(q2); 76 | l.push_back(q3); 77 | l.push_back(q4); 78 | 79 | std::cout << isSortedSequence(l); 80 | } 81 | -------------------------------------------------------------------------------- /Sem_05/Solutions/Task04.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | bool areQueuesOfSameSize(const std::queue& lhs, const std::queue& rhs) 6 | { 7 | return lhs.size() == rhs.size(); 8 | } 9 | 10 | void mergeQueues(std::queue& lhs, std::queue& rhs) 11 | { 12 | while (!rhs.empty()) 13 | { 14 | lhs.push(rhs.front()); 15 | rhs.pop(); 16 | } 17 | } 18 | 19 | void equalize(std::queue& lhs, std::queue& rhs) 20 | { 21 | bool isLhsBigger = lhs.size() > rhs.size() ? true : false; 22 | 23 | if (isLhsBigger) 24 | { 25 | while (lhs.size() > rhs.size() + 1) 26 | { 27 | rhs.push(lhs.front()); 28 | lhs.pop(); 29 | } 30 | } 31 | else 32 | { 33 | while (rhs.size() > lhs.size() + 1) 34 | { 35 | lhs.push(rhs.front()); 36 | rhs.pop(); 37 | } 38 | } 39 | } 40 | 41 | void printList(const std::list>& l) 42 | { 43 | for (const auto& q : l) 44 | { 45 | std::queue temp = q; // Create a copy of the std::queue to print its elements 46 | while (!temp.empty()) 47 | { 48 | std::cout << temp.front() << " "; 49 | temp.pop(); 50 | } 51 | std::cout << std::endl; 52 | } 53 | } 54 | 55 | void modifyList(std::list>& l) 56 | { 57 | if (l.empty()) 58 | return; 59 | 60 | std::list>::iterator it = l.begin(); 61 | 62 | while (it != l.end()) 63 | { 64 | std::list>::iterator next = it; 65 | ++next; 66 | 67 | if (next == l.end()) 68 | break; 69 | 70 | if (areQueuesOfSameSize(*it, *next)) 71 | { 72 | mergeQueues(*it, *next); 73 | l.erase(next); 74 | continue; 75 | } 76 | else 77 | { 78 | ++it; 79 | 80 | if (++next == l.end()) 81 | break; 82 | 83 | if (!areQueuesOfSameSize(*it, *next)) 84 | { 85 | equalize(*it, *next); 86 | ++it; 87 | } 88 | } 89 | } 90 | } 91 | 92 | int main() 93 | { 94 | std::list> l; 95 | 96 | std::queue q1; 97 | q1.push(2); 98 | q1.push(4); 99 | q1.push(6); 100 | std::queue q2; 101 | q2.push(8); 102 | q2.push(0); 103 | q2.push(7); 104 | std::queue q3; 105 | q3.push(5); 106 | q3.push(7); 107 | q3.push(9); 108 | q3.push(23); 109 | q3.push(22); 110 | std::queue q4; 111 | q4.push(9); 112 | q4.push(1); 113 | std::queue q5; 114 | q5.push(2); 115 | std::queue q6; 116 | q6.push(1); 117 | std::queue q7; 118 | q7.push(33); 119 | q7.push(44); 120 | 121 | l.push_back(q1); 122 | l.push_back(q2); 123 | l.push_back(q3); 124 | l.push_back(q4); 125 | l.push_back(q5); 126 | l.push_back(q6); 127 | l.push_back(q7); 128 | 129 | std::cout << "Before modifyList:" << std::endl; 130 | printList(l); 131 | 132 | modifyList(l); 133 | 134 | std::cout << "After modifyList:" << std::endl; 135 | printList(l); 136 | } 137 | -------------------------------------------------------------------------------- /Sem_05/Stack/ArrayStack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace Constants 5 | { 6 | constexpr unsigned GROWTH_FACTOR = 2; 7 | } 8 | 9 | template > 10 | class stack 11 | { 12 | public: 13 | stack() = default; 14 | 15 | stack(const stack& other); 16 | stack& operator=(const stack& other); 17 | 18 | stack(stack&& other) noexcept; 19 | stack& operator=(stack&& other) noexcept; 20 | 21 | ~stack() noexcept; 22 | 23 | void push(const T& element); 24 | void push(T&& element); 25 | 26 | template 27 | T emplace(Args&&... args); 28 | 29 | void pop(); 30 | 31 | size_t getSize() const; 32 | bool empty() const; 33 | 34 | const T& top() const; 35 | T& top(); 36 | 37 | private: 38 | T* data = nullptr; 39 | size_t size = 0; 40 | size_t capacity = 0; 41 | 42 | Allocator myAlloc; 43 | 44 | void copyFrom(const stack& other); 45 | void free(); 46 | void moveFrom(stack&& other); 47 | void reserve(size_t n); 48 | size_t calculateCapacity() const; 49 | }; 50 | 51 | template 52 | void stack::push(const T& element) 53 | { 54 | if (size == capacity) 55 | { 56 | reserve(calculateCapacity()); 57 | } 58 | std::construct_at(data[size++], element); 59 | } 60 | 61 | template 62 | void stack::push(T&& element) 63 | { 64 | if (size == capacity) 65 | { 66 | reserve(calculateCapacity()); 67 | } 68 | std::construct_at(&data[size++], std::move(element)); 69 | } 70 | 71 | template 72 | template 73 | T stack::emplace(Args&&... args) 74 | { 75 | T newObj(std::forward(args)...); 76 | if (size == capacity) 77 | { 78 | reserve(calculateCapacity()); 79 | } 80 | std::construct_at(&data[size++], std::forward(args)...); 81 | return newObj; 82 | } 83 | 84 | template 85 | void stack::pop() 86 | { 87 | if (empty()) 88 | { 89 | throw std::logic_error("Empty stack\n"); 90 | } 91 | std::destroy_at(&data[--size]); 92 | } 93 | 94 | template 95 | const T& stack::top() const 96 | { 97 | return data[size - 1]; 98 | } 99 | 100 | template 101 | T& stack::top() 102 | { 103 | return data[size - 1]; 104 | } 105 | 106 | template 107 | size_t stack::getSize() const 108 | { 109 | return size; 110 | } 111 | 112 | template 113 | bool stack::empty() const 114 | { 115 | return size == 0; 116 | } 117 | 118 | template 119 | stack::stack(const stack& other) 120 | { 121 | copyFrom(other); 122 | } 123 | 124 | template 125 | stack& stack::operator=(const stack& other) 126 | { 127 | if (this != &other) 128 | { 129 | free(); 130 | copyFrom(other); 131 | } 132 | return *this; 133 | } 134 | 135 | template 136 | stack::stack(stack&& other) noexcept 137 | { 138 | moveFrom(std::move(other)); 139 | } 140 | 141 | template 142 | stack& stack::operator=(stack&& other) noexcept 143 | { 144 | if (this != &other) 145 | { 146 | free(); 147 | moveFrom(std::move(other)); 148 | } 149 | return *this; 150 | } 151 | 152 | template 153 | stack::~stack() noexcept 154 | { 155 | free(); 156 | } 157 | 158 | template 159 | void stack::copyFrom(const stack& other) 160 | { 161 | data = myAlloc.allocate(other.capacity); 162 | for (size_t i = 0; i < other.size; i++) 163 | { 164 | std::construct_at(&data[i], other[i]); 165 | } 166 | size = other.getSize(); 167 | capacity = other.capacity(); 168 | } 169 | 170 | template 171 | void stack::moveFrom(stack&& other) 172 | { 173 | size = other.size; 174 | capacity = other.capacity; 175 | data = other.data; 176 | 177 | other.data = nullptr; 178 | other.size = other.capacity = 0; 179 | } 180 | 181 | template 182 | void stack::free() 183 | { 184 | for (size_t i = 0; i < size; i++) 185 | { 186 | std::destroy_at(&data[i]); 187 | } 188 | myAlloc.deallocate(data, capacity); 189 | } 190 | 191 | template 192 | void stack::reserve(size_t n) 193 | { 194 | if (n <= capacity) 195 | { 196 | return; 197 | } 198 | T* newdata = myAlloc.allocate(n); 199 | for (size_t i = 0; i < size; i++) 200 | { 201 | std::construct_at(&newdata[i], std::move(data[i])); 202 | } 203 | myAlloc.deallocate(data, capacity); 204 | 205 | data = newdata; 206 | capacity = n; 207 | } 208 | 209 | template 210 | size_t stack::calculateCapacity() const 211 | { 212 | if (capacity == 0) 213 | { 214 | return 1; 215 | } 216 | return capacity * Constants::GROWTH_FACTOR; 217 | } 218 | -------------------------------------------------------------------------------- /Sem_05/Stack/CheckValidBracketsString.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | bool isOpeningBracket(char ch) 6 | { 7 | return ch == '(' || ch == '[' || ch == '{'; 8 | } 9 | 10 | bool isMatchingBracket(char ch1, char ch2) 11 | { 12 | switch (ch1) 13 | { 14 | case '(': 15 | return ch2 == ')'; 16 | case '[': 17 | return ch2 == ']'; 18 | case '{': 19 | return ch2 == '}'; 20 | } 21 | return false; 22 | } 23 | 24 | bool isCorrect(const std::string& str) 25 | { 26 | std::stack brackets; 27 | 28 | for (char ch : str) 29 | { 30 | if (isOpeningBracket(ch)) 31 | { 32 | brackets.push(ch); 33 | } 34 | else 35 | { 36 | if (brackets.empty() || !isMatchingBracket(brackets.top(), ch)) 37 | { 38 | return false; 39 | } 40 | else 41 | { 42 | brackets.pop(); 43 | } 44 | } 45 | } 46 | return brackets.empty(); 47 | } 48 | 49 | int main() 50 | { 51 | std::cout << isCorrect("()"); 52 | } 53 | -------------------------------------------------------------------------------- /Sem_05/Stack/LinkedStack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | template 5 | class stack 6 | { 7 | struct Node 8 | { 9 | Node(const T& data) : data(data), next(nullptr) {} 10 | Node(T&& data) : data(std::move(data)), next(nullptr) {} 11 | 12 | T data; 13 | Node* next; 14 | }; 15 | 16 | public: 17 | stack() = default; 18 | 19 | stack(const stack& other); 20 | stack& operator=(const stack& other); 21 | 22 | stack(stack&& other) noexcept; 23 | stack& operator=(stack&& other) noexcept; 24 | 25 | ~stack() noexcept; 26 | 27 | void push(const T& el); 28 | void push(T&& el); 29 | 30 | template 31 | T emplace(Args&&... args); 32 | 33 | void pop(); 34 | 35 | const T& top() const; 36 | T& top(); 37 | 38 | size_t getSize() const; 39 | bool empty() const; 40 | 41 | private: 42 | Node* head = nullptr; 43 | Node* tail = nullptr; 44 | size_t size = 0; 45 | 46 | void copyFrom(const stack& other); 47 | void moveFrom(stack&& other); 48 | void free(); 49 | }; 50 | 51 | template 52 | void stack::push(const T& el) 53 | { 54 | Node* newNode = new Node(el); 55 | if (empty()) 56 | { 57 | head = tail = newNode; 58 | } 59 | else 60 | { 61 | newNode->next = head; 62 | head = newNode; 63 | } 64 | size++; 65 | } 66 | 67 | template 68 | void stack::push(T&& el) 69 | { 70 | Node* newNode = new Node(std::move(el)); 71 | if (empty()) 72 | { 73 | head = tail = newNode; 74 | } 75 | else 76 | { 77 | newNode->next = head; 78 | head = newNode; 79 | } 80 | size++; 81 | } 82 | 83 | template 84 | template 85 | T stack::emplace(Args&&... args) 86 | { 87 | T newObj(std::forward(args)...); 88 | Node* newNode = new Node(newObj); 89 | if (empty()) 90 | { 91 | head = tail = newNode; 92 | } 93 | else 94 | { 95 | newNode->next = head; 96 | head = newNode; 97 | } 98 | size++; 99 | return newObj; 100 | } 101 | 102 | template 103 | void stack::pop() 104 | { 105 | if (!head) 106 | { 107 | throw std::logic_error("Empty stack\n"); 108 | } 109 | 110 | if (head == tail) 111 | { 112 | delete head; 113 | head = tail = nullptr; 114 | } 115 | else 116 | { 117 | Node* toDelete = head; 118 | head = head->next; 119 | delete toDelete; 120 | } 121 | size--; 122 | } 123 | 124 | template 125 | const T& stack::top() const 126 | { 127 | if (!head) 128 | { 129 | throw std::logic_error("Empty stack\n"); 130 | } 131 | return head->data; 132 | } 133 | 134 | template 135 | T& stack::top() 136 | { 137 | if (!head) 138 | { 139 | throw std::logic_error("Empty stack\n"); 140 | } 141 | return head->data; 142 | } 143 | 144 | template 145 | size_t stack::getSize() const 146 | { 147 | return size; 148 | } 149 | 150 | template 151 | bool stack::empty() const 152 | { 153 | return size == 0; 154 | } 155 | 156 | template 157 | stack::stack(const stack& other) 158 | { 159 | copyFrom(other); 160 | } 161 | 162 | template 163 | stack& stack::operator=(const stack& other) 164 | { 165 | if (this != &other) 166 | { 167 | free(); 168 | copyFrom(other); 169 | } 170 | return *this; 171 | } 172 | 173 | template 174 | stack::stack(stack&& other) noexcept 175 | { 176 | moveFrom(std::move(other)); 177 | } 178 | 179 | template 180 | stack& stack::operator=(stack&& other) noexcept 181 | { 182 | if (this != &other) 183 | { 184 | free(); 185 | moveFrom(std::move(other)); 186 | } 187 | return *this; 188 | } 189 | 190 | template 191 | stack::~stack() noexcept 192 | { 193 | free(); 194 | } 195 | 196 | template 197 | void stack::copyFrom(const stack& other) 198 | { 199 | Node* iter = other.head; 200 | while (iter) 201 | { 202 | push_back(iter->data); 203 | iter = iter->next; 204 | } 205 | } 206 | 207 | template 208 | void stack::moveFrom(stack&& other) 209 | { 210 | head = other.head; 211 | tail = other.tail; 212 | size = other.size; 213 | other.head = other.tail = nullptr; 214 | other.size = 0; 215 | } 216 | 217 | template 218 | void stack::free() 219 | { 220 | Node* iter = head; 221 | while (iter) 222 | { 223 | Node* toDelete = iter; 224 | iter = iter->next; 225 | delete toDelete; 226 | } 227 | head = tail = nullptr; 228 | size = 0; 229 | } 230 | -------------------------------------------------------------------------------- /Sem_05/Stack/StackWithTemplateContainer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "deque.hpp" 3 | 4 | template > 5 | class stack 6 | { 7 | public: 8 | void push(const T& el) 9 | { 10 | c.push_back(el); 11 | } 12 | void pop() 13 | { 14 | c.pop_back(); 15 | } 16 | 17 | T& top() 18 | { 19 | return c.back(); 20 | } 21 | const T& top() const 22 | { 23 | return c.back(); 24 | } 25 | 26 | bool empty() const 27 | { 28 | return c.empty(); 29 | } 30 | size_t size() const 31 | { 32 | return c.size(); 33 | } 34 | 35 | private: 36 | Container c; 37 | }; 38 | -------------------------------------------------------------------------------- /Sem_06/ParentArrayTreeHeight.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::vector getNextLevel(const std::vector& currLevel, const std::vector& parentArray) 6 | { 7 | std::vector nextLevel; 8 | for (size_t i = 0; i < parentArray.size(); i++) 9 | { 10 | int currVertexParent = parentArray[i]; 11 | auto it = std::find(currLevel.cbegin(), currLevel.cend(), currVertexParent); 12 | if (it != currLevel.cend()) 13 | { 14 | nextLevel.push_back(i); 15 | } 16 | } 17 | return nextLevel; 18 | } 19 | 20 | int getHeight(const std::vector& parentArray) 21 | { 22 | std::vector currentLevel = { -1 }; 23 | int level = -1; 24 | while (!currentLevel.empty()) 25 | { 26 | level++; 27 | currentLevel = getNextLevel(currentLevel, parentArray); 28 | } 29 | return level - 1; 30 | } 31 | 32 | int main() 33 | { 34 | std::vector v = { -1, 0, 1, 2 }; 35 | std::cout << getHeight(v); 36 | } 37 | -------------------------------------------------------------------------------- /Sem_06/README.md: -------------------------------------------------------------------------------- 1 | # Йерархични структури от данни (Hierarchical data structures) 2 | 3 | Йерархичните структури от данни имат главна стойност, която се нарича корен (root), от която тръгват разклонения от възли, които оформят йерархична връзка на принципа parent-child. Всеки възел, от който произлизат едно или повече разклонения, се води родител или parent, а подчинените му възли са деца. По този начин се създава сложна разклоняваща се структура. 4 | 5 | #### Наредба на елементите при линейни и йерархични структури: 6 | Нека R е релация (елементите й са от вида ) и ∈ R <=> x е предшественик на y. Тогава: 7 | Множеството от елементите на йерархичните структури, заедно с релацията "предшествие" образуват ***частично наредено множество*** (не всеки два елемента могат да бъдат сравнени по критерия "предшествие"). 8 | Множеството от елементите на линейните структури от данни, заедно с релацията "предшествие" образуват ***линейно наредено множество*** (нямаме несравними елементи по критерия "предшествие"). 9 | 10 | ## Дървета 11 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_06/images/tree.png) 12 | 13 | ### Двоични дървета 14 | Ще ги представяме със следната структура 15 | ```c++ 16 | template 17 | struct Node 18 | { 19 | T data; 20 | 21 | Node* left; 22 | Node* right; 23 | }; 24 | ``` 25 | ### N-ични дървета 26 | Ще ги представяме със следната структура 27 | ```c++ 28 | template 29 | struct Node 30 | { 31 | T data; 32 | 33 | std::vector*> children; 34 | }; 35 | ``` 36 | 37 | ## Задачи 38 | 39 | **Задача 1:** Да се напише функция, която приема указател към корена на двоично дърво и елемент и връща *дали елементът се съдържа в него*. 40 | 41 | **Задача 2:** Да се напише функция, която приема указател към корена на двоично дърво от числа и връща *сумата на числата в него*. 42 | 43 | **Задача 3:** Да се напише функция, която приема указател към корена на двоично дърво и връща *най-големия елемент в него*. 44 | 45 | **Задача 4:** Да се напише функция, която приема указател към корена на двоично дърво и връща *височината му*. 46 | 47 | **Задача 5:** Да се напише функция, която приема указател към корена на двоично дърво и връща *броя на елементите му*. 48 | 49 | **Задача 6:** Да се напише функция, която приема указател към корена на двоично дърво с n върха и връща *дали числата от 1 до n се срещат точно веднъж в дървото*. 50 | 51 | **Задача 7:** Да се напише функция, която приема указател към корена на двоично дърво, чиито елементи са символи, и цяло число k и връща *думата на k-тото ниво на дървото*. 52 | 53 | **Задача 8:** Да се напише функция, която приема указател към корена на произволно дърво, чиито елементи са символи, и връща *думите от всички нива на дървото*. 54 | 55 | **Задача 9:** Да се напише функция, която приема указател към корена на двоично дърво и връща *всички думи, които се получават обхождайки дървото от корена до някое листо*. 56 | 57 | **Задача 10:** Да се напише функция, която приема указател към корена на двоично дърво и връща *сумата на листата му*. 58 | 59 | **Задача 11:** Напишете функция, която приема указател към корена на двоично дърво и *обхожда елементите му по следния начин: ляво - корен - дясно (Inorder Traversal)*. 60 | 61 | **Задача 12:** Напишете функция, която приема указател към корена на двоично дърво и *променя дървото в неговата огледална форма*. 62 | 63 | **Задача 13:** Напишете функция, която *намира височината на произволно дърво, представено чрез масив от родители (parent array)*. 64 | 65 | **Задача 14:** 66 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_06/images/tripleTree.png) 67 | -------------------------------------------------------------------------------- /Sem_06/Task01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | 8 | Node* left; 9 | Node* right; 10 | 11 | Node(const T& data) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | template 27 | bool containsElement(Node* root, const T& element) 28 | { 29 | if (!root) 30 | { 31 | return false; 32 | } 33 | if (root->data == element) 34 | { 35 | return true; 36 | } 37 | return containsElement(root->left, element) || containsElement(root->right, element); 38 | } 39 | 40 | int main() 41 | { 42 | Node* root = new Node(2); 43 | Node* n1 = new Node(7); 44 | Node* n2 = new Node(5); 45 | Node* n3 = new Node(2); 46 | Node* n4 = new Node(6); 47 | Node* n5 = new Node(9); 48 | Node* n6 = new Node(5); 49 | Node* n7 = new Node(11); 50 | Node* n8 = new Node(4); 51 | 52 | root->left = n1; 53 | root->right = n2; 54 | n1->left = n3; 55 | n1->right = n4; 56 | n2->right = n5; 57 | n4->left = n6; 58 | n4->right = n7; 59 | n5->left = n8; 60 | 61 | std::cout << containsElement(root, 4); 62 | free(root); 63 | } 64 | -------------------------------------------------------------------------------- /Sem_06/Task02.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Node 4 | { 5 | int data; 6 | 7 | Node* left; 8 | Node* right; 9 | 10 | Node(int data) : data(data), left(nullptr), right(nullptr) {} 11 | }; 12 | 13 | void free(Node* root) 14 | { 15 | if (!root) 16 | { 17 | return; 18 | } 19 | free(root->left); 20 | free(root->right); 21 | delete root; 22 | } 23 | 24 | int sum(Node* root) 25 | { 26 | if (!root) 27 | { 28 | return 0; 29 | } 30 | return root->data + sum(root->left) + sum(root->right); 31 | } 32 | 33 | int main() 34 | { 35 | Node* root = new Node(2); 36 | Node* n1 = new Node(7); 37 | Node* n2 = new Node(5); 38 | Node* n3 = new Node(2); 39 | Node* n4 = new Node(6); 40 | Node* n5 = new Node(9); 41 | Node* n6 = new Node(5); 42 | Node* n7 = new Node(11); 43 | Node* n8 = new Node(4); 44 | 45 | root->left = n1; 46 | root->right = n2; 47 | n1->left = n3; 48 | n1->right = n4; 49 | n2->right = n5; 50 | n4->left = n6; 51 | n4->right = n7; 52 | n5->left = n8; 53 | 54 | std::cout << sum(root); 55 | free(root); 56 | } 57 | -------------------------------------------------------------------------------- /Sem_06/Task03.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template 6 | struct Node 7 | { 8 | T data; 9 | 10 | Node* left; 11 | Node* right; 12 | 13 | Node(const T& data) : data(data), left(nullptr), right(nullptr) {} 14 | }; 15 | 16 | template 17 | void free(Node* root) 18 | { 19 | if (!root) 20 | { 21 | return; 22 | } 23 | free(root->left); 24 | free(root->right); 25 | delete root; 26 | } 27 | 28 | template 29 | const T& getMaxElement(Node* root, const Func& comp) 30 | { 31 | if (!root) 32 | { 33 | throw std::invalid_argument("Empty tree"); 34 | } 35 | if (!root->left && !root->right) 36 | { 37 | return root->data; 38 | } 39 | if (!root->left) 40 | { 41 | return std::max(root->data, getMaxElement(root->right, comp), comp); 42 | } 43 | if (!root->right) 44 | { 45 | return std::max(root->data, getMaxElement(root->left, comp), comp); 46 | } 47 | return std::max(root->data, std::max(getMaxElement(root->left, comp), getMaxElement(root->right, comp), comp), comp); 48 | } 49 | 50 | int main() 51 | { 52 | Node* root = new Node(2); 53 | Node* n1 = new Node(7); 54 | Node* n2 = new Node(5); 55 | Node* n3 = new Node(2); 56 | Node* n4 = new Node(6); 57 | Node* n5 = new Node(9); 58 | Node* n6 = new Node(5); 59 | Node* n7 = new Node(11); 60 | Node* n8 = new Node(4); 61 | 62 | root->left = n1; 63 | root->right = n2; 64 | n1->left = n3; 65 | n1->right = n4; 66 | n2->right = n5; 67 | n4->left = n6; 68 | n4->right = n7; 69 | n5->left = n8; 70 | 71 | std::cout << getMaxElement(root, [](int a, int b) { return a < b; }); 72 | free(root); 73 | } 74 | -------------------------------------------------------------------------------- /Sem_06/Task04.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | struct Node 6 | { 7 | T data; 8 | 9 | Node* left; 10 | Node* right; 11 | 12 | Node(const T& data) : data(data), left(nullptr), right(nullptr) {} 13 | }; 14 | 15 | template 16 | void free(Node* root) 17 | { 18 | if (!root) 19 | { 20 | return; 21 | } 22 | free(root->left); 23 | free(root->right); 24 | delete root; 25 | } 26 | 27 | template 28 | unsigned getHeight(Node* root) 29 | { 30 | if (!root) 31 | { 32 | return -1; 33 | } 34 | return 1 + std::max(getHeight(root->left), getHeight(root->right)); 35 | } 36 | 37 | int main() 38 | { 39 | Node* root = new Node(2); 40 | Node* n1 = new Node(7); 41 | Node* n2 = new Node(5); 42 | Node* n3 = new Node(2); 43 | Node* n4 = new Node(6); 44 | Node* n5 = new Node(9); 45 | Node* n6 = new Node(5); 46 | Node* n7 = new Node(11); 47 | Node* n8 = new Node(4); 48 | 49 | root->left = n1; 50 | root->right = n2; 51 | n1->left = n3; 52 | n1->right = n4; 53 | n2->right = n5; 54 | n4->left = n6; 55 | n4->right = n7; 56 | n5->left = n8; 57 | 58 | std::cout << getHeight(root); 59 | free(root); 60 | } 61 | -------------------------------------------------------------------------------- /Sem_06/Task05.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | 8 | Node* left; 9 | Node* right; 10 | 11 | Node(const T& data) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | template 27 | unsigned getElementsCount(Node* root) 28 | { 29 | if (!root) 30 | { 31 | return 0; 32 | } 33 | return 1 + (getElementsCount(root->left) + getElementsCount(root->right)); 34 | } 35 | 36 | int main() 37 | { 38 | Node* root = new Node(2); 39 | Node* n1 = new Node(7); 40 | Node* n2 = new Node(5); 41 | Node* n3 = new Node(2); 42 | Node* n4 = new Node(6); 43 | Node* n5 = new Node(9); 44 | Node* n6 = new Node(5); 45 | Node* n7 = new Node(11); 46 | Node* n8 = new Node(4); 47 | 48 | root->left = n1; 49 | root->right = n2; 50 | n1->left = n3; 51 | n1->right = n4; 52 | n2->right = n5; 53 | n4->left = n6; 54 | n4->right = n7; 55 | n5->left = n8; 56 | 57 | std::cout << getElementsCount(root); 58 | free(root); 59 | } 60 | -------------------------------------------------------------------------------- /Sem_06/Task06.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Node 5 | { 6 | int data; 7 | 8 | Node* left; 9 | Node* right; 10 | 11 | Node(int data) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | void free(Node* root) 15 | { 16 | if (!root) 17 | { 18 | return; 19 | } 20 | free(root->left); 21 | free(root->right); 22 | delete root; 23 | } 24 | 25 | unsigned getElementsCount(Node* root) 26 | { 27 | if (!root) 28 | { 29 | return 0; 30 | } 31 | return 1 + (getElementsCount(root->left) + getElementsCount(root->right)); 32 | } 33 | 34 | bool checkIfAllNumbersAreInTree(Node* root, std::vector& numbers) 35 | { 36 | if (!root) 37 | { 38 | return true; 39 | } 40 | if ((root->data < 1 || root->data > numbers.size()) || numbers[root->data - 1]) 41 | { 42 | return false; 43 | } 44 | numbers[root->data - 1] = true; 45 | return checkIfAllNumbersAreInTree(root->left, numbers) && checkIfAllNumbersAreInTree(root->right, numbers); 46 | } 47 | 48 | bool isPermutation(Node* root) 49 | { 50 | unsigned size = getElementsCount(root); 51 | std::vector numbers(size, 0); 52 | return checkIfAllNumbersAreInTree(root, numbers); 53 | } 54 | 55 | int main() 56 | { 57 | Node* root = new Node(2); 58 | Node* n1 = new Node(7); 59 | Node* n2 = new Node(5); 60 | Node* n3 = new Node(1); 61 | Node* n4 = new Node(6); 62 | Node* n5 = new Node(9); 63 | Node* n6 = new Node(3); 64 | Node* n7 = new Node(8); 65 | Node* n8 = new Node(4); 66 | 67 | root->left = n1; 68 | root->right = n2; 69 | n1->left = n3; 70 | n1->right = n4; 71 | n2->right = n5; 72 | n4->left = n6; 73 | n4->right = n7; 74 | n5->left = n8; 75 | 76 | std::cout << isPermutation(root); 77 | free(root); 78 | } 79 | -------------------------------------------------------------------------------- /Sem_06/Task07.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Node 6 | { 7 | char data; 8 | 9 | Node* left; 10 | Node* right; 11 | 12 | Node(char data) : data(data), left(nullptr), right(nullptr) {} 13 | }; 14 | 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | // First way 27 | void createWord(Node* root, std::string& word, int k) 28 | { 29 | if (!root) 30 | { 31 | return; 32 | } 33 | if (k == 1) 34 | { 35 | word.push_back(root->data); 36 | return; 37 | } 38 | createWord(root->left, word, k - 1); 39 | createWord(root->right, word, k - 1); 40 | } 41 | 42 | std::string getWordOnLevel(Node* root, int k) 43 | { 44 | if (!root || k == 0) 45 | { 46 | throw std::invalid_argument("Invalid parameter"); 47 | } 48 | 49 | std::string word; 50 | createWord(root, word, k); 51 | return word; 52 | } 53 | 54 | // Second way 55 | std::string getWordOnLevel2(Node* root, unsigned k) 56 | { 57 | if (!root || k == 0) 58 | { 59 | throw std::invalid_argument("Invalid parameter"); 60 | } 61 | if (k == 1) 62 | { 63 | return std::string(1, root->data); 64 | } 65 | return getWordOnLevel2(root->left, k - 1) + getWordOnLevel2(root->right, k - 1); 66 | } 67 | 68 | int main() 69 | { 70 | Node* root = new Node('a'); 71 | Node* n1 = new Node('b'); 72 | Node* n2 = new Node('c'); 73 | Node* n3 = new Node('d'); 74 | Node* n4 = new Node('e'); 75 | Node* n5 = new Node('f'); 76 | Node* n6 = new Node('g'); 77 | Node* n7 = new Node('h'); 78 | Node* n8 = new Node('i'); 79 | 80 | root->left = n1; 81 | root->right = n2; 82 | n1->left = n3; 83 | n1->right = n4; 84 | n2->right = n5; 85 | n4->left = n6; 86 | n4->right = n7; 87 | n5->left = n8; 88 | 89 | std::cout << getWordOnLevel(root, 3); 90 | free(root); 91 | } 92 | -------------------------------------------------------------------------------- /Sem_06/Task08.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct Node 7 | { 8 | char data; 9 | 10 | std::vector children; 11 | 12 | Node(char data) : data(data) {} 13 | }; 14 | 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | for (Node* child : root->children) 22 | { 23 | free(child); 24 | } 25 | delete root; 26 | } 27 | 28 | void printByLevels(Node* root) 29 | { 30 | std::vector currLevel{ root }; 31 | 32 | while (!currLevel.empty()) 33 | { 34 | std::vector nextLevel; 35 | for (const Node* curr : currLevel) 36 | { 37 | std::cout << curr->data; 38 | for (const Node* child : curr->children) 39 | { 40 | nextLevel.push_back(child); 41 | } 42 | } 43 | std::cout << std::endl; 44 | currLevel = nextLevel; 45 | } 46 | } 47 | 48 | int main() 49 | { 50 | Node* root = new Node{ 'a' }; 51 | root->children.push_back(new Node{'b'}); 52 | root->children.push_back(new Node{ 'c' }); 53 | root->children.push_back(new Node{ 'd' }); 54 | root->children.at(0)->children.push_back(new Node{ 'x' }); 55 | root->children.at(0)->children.push_back(new Node{ 'y' }); 56 | 57 | printByLevels(root); 58 | free(root); 59 | } 60 | -------------------------------------------------------------------------------- /Sem_06/Task09.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct Node 7 | { 8 | char data; 9 | 10 | Node* left; 11 | Node* right; 12 | 13 | Node(char data) : data(data), left(nullptr), right(nullptr) {} 14 | }; 15 | 16 | void free(Node* root) 17 | { 18 | if (!root) 19 | { 20 | return; 21 | } 22 | free(root->left); 23 | free(root->right); 24 | delete root; 25 | } 26 | 27 | void fillStrings(Node* root, std::vector& words, std::string currentWord) 28 | { 29 | if (!root) 30 | { 31 | return; 32 | } 33 | 34 | currentWord.push_back(root->data); 35 | if (!root->left && !root->right) 36 | { 37 | words.push_back(currentWord); 38 | return; 39 | } 40 | fillStrings(root->left, words, currentWord); 41 | fillStrings(root->right, words, currentWord); 42 | } 43 | 44 | std::vector getWords(Node* root) 45 | { 46 | std::vector words; 47 | std::string currentWord = ""; 48 | fillStrings(root, words, currentWord); 49 | return words; 50 | } 51 | 52 | int main() 53 | { 54 | Node* root = new Node('a'); 55 | Node* n1 = new Node('b'); 56 | Node* n2 = new Node('c'); 57 | Node* n3 = new Node('d'); 58 | Node* n4 = new Node('e'); 59 | Node* n5 = new Node('f'); 60 | Node* n6 = new Node('g'); 61 | Node* n7 = new Node('h'); 62 | Node* n8 = new Node('i'); 63 | 64 | root->left = n1; 65 | root->right = n2; 66 | n1->left = n3; 67 | n1->right = n4; 68 | n2->right = n5; 69 | n4->left = n6; 70 | n4->right = n7; 71 | n5->left = n8; 72 | 73 | std::vector words = getWords(root); 74 | for (size_t i = 0; i < words.size(); i++) 75 | { 76 | std::cout << words[i] << std::endl; 77 | } 78 | free(root); 79 | } 80 | -------------------------------------------------------------------------------- /Sem_06/Task10.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Node 4 | { 5 | int data; 6 | 7 | Node* left; 8 | Node* right; 9 | 10 | Node(int data) : data(data), left(nullptr), right(nullptr) {} 11 | }; 12 | 13 | void free(Node* root) 14 | { 15 | if (!root) 16 | { 17 | return; 18 | } 19 | free(root->left); 20 | free(root->right); 21 | delete root; 22 | } 23 | 24 | int sumOfLeaves(Node* root) 25 | { 26 | if (!root) 27 | { 28 | return 0; 29 | } 30 | if (!root->left && !root->right) 31 | { 32 | return root->data; 33 | } 34 | return sumOfLeaves(root->left) + sumOfLeaves(root->right); 35 | } 36 | 37 | int main() 38 | { 39 | Node* root = new Node(2); 40 | Node* n1 = new Node(7); 41 | Node* n2 = new Node(5); 42 | Node* n3 = new Node(2); 43 | Node* n4 = new Node(6); 44 | Node* n5 = new Node(9); 45 | Node* n6 = new Node(5); 46 | Node* n7 = new Node(11); 47 | Node* n8 = new Node(4); 48 | 49 | root->left = n1; 50 | root->right = n2; 51 | n1->left = n3; 52 | n1->right = n4; 53 | n2->right = n5; 54 | n4->left = n6; 55 | n4->right = n7; 56 | n5->left = n8; 57 | 58 | std::cout << sumOfLeaves(root); 59 | free(root); 60 | } 61 | -------------------------------------------------------------------------------- /Sem_06/Task11.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | 8 | Node* left; 9 | Node* right; 10 | 11 | Node(const T& data) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | template 27 | void print(Node* root) 28 | { 29 | if (!root) 30 | { 31 | return; 32 | } 33 | print(root->left); 34 | std::cout << root->data << " "; 35 | print(root->right); 36 | } 37 | 38 | template 39 | void inorderTraversal(Node* root) 40 | { 41 | print(root); 42 | } 43 | 44 | int main() 45 | { 46 | Node* root = new Node(2); 47 | Node* n1 = new Node(7); 48 | Node* n2 = new Node(5); 49 | Node* n3 = new Node(2); 50 | Node* n4 = new Node(6); 51 | Node* n5 = new Node(9); 52 | Node* n6 = new Node(5); 53 | Node* n7 = new Node(11); 54 | Node* n8 = new Node(4); 55 | 56 | root->left = n1; 57 | root->right = n2; 58 | n1->left = n3; 59 | n1->right = n4; 60 | n2->right = n5; 61 | n4->left = n6; 62 | n4->right = n7; 63 | n5->left = n8; 64 | 65 | inorderTraversal(root); 66 | free(root); 67 | } 68 | -------------------------------------------------------------------------------- /Sem_06/Task12.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | struct Node 5 | { 6 | T data; 7 | 8 | Node* left; 9 | Node* right; 10 | 11 | Node(const T& data) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | template 27 | void swap(Node*& a, Node*& b) 28 | { 29 | Node* temp = a; 30 | a = b; 31 | b = temp; 32 | } 33 | 34 | template 35 | void mirror(Node* root) 36 | { 37 | if (!root) 38 | { 39 | return; 40 | } 41 | 42 | swap(root->left, root->right); 43 | mirror(root->left); 44 | mirror(root->right); 45 | } 46 | 47 | int main() 48 | { 49 | Node* root = new Node(2); 50 | Node* n1 = new Node(7); 51 | Node* n2 = new Node(5); 52 | Node* n3 = new Node(2); 53 | Node* n4 = new Node(6); 54 | Node* n5 = new Node(9); 55 | Node* n6 = new Node(5); 56 | Node* n7 = new Node(11); 57 | Node* n8 = new Node(4); 58 | 59 | root->left = n1; 60 | root->right = n2; 61 | n1->left = n3; 62 | n1->right = n4; 63 | n2->right = n5; 64 | n4->left = n6; 65 | n4->right = n7; 66 | n5->left = n8; 67 | 68 | mirror(root); 69 | free(root); 70 | } 71 | -------------------------------------------------------------------------------- /Sem_06/TripleTree/TripleTree.txt: -------------------------------------------------------------------------------- 1 | (b (x (p * * *) (q * * *) (r (c * * *) * (a * * *))) (y * * (s * (t * * *) *)) *) -------------------------------------------------------------------------------- /Sem_06/images/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_06/images/tree.png -------------------------------------------------------------------------------- /Sem_06/images/tripleTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_06/images/tripleTree.png -------------------------------------------------------------------------------- /Sem_07/Binary search tree/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "BST.hpp" 2 | 3 | int main() 4 | { 5 | BST bst; 6 | 7 | bst.insert(2.5); 8 | bst.insert(1); 9 | bst.insert(2); 10 | bst.insert(4); 11 | bst.insert(3); 12 | bst.insert(5); 13 | 14 | std::cout << "Initial BST:" << std::endl; 15 | bst.print(); 16 | std::cout << "------------------------------" << std::endl; 17 | 18 | bst.remove(2.5); 19 | std::cout << "After removing 2.5:" << std::endl; 20 | bst.print(); 21 | std::cout << "------------------------------" << std::endl; 22 | 23 | bst.remove(4); 24 | std::cout << "After removing 4:" << std::endl; 25 | bst.print(); 26 | std::cout << "------------------------------" << std::endl; 27 | 28 | bst.remove(2); 29 | std::cout << "After removing 2:" << std::endl; 30 | bst.print(); 31 | std::cout << "------------------------------" << std::endl; 32 | 33 | BST bst2 = bst; 34 | std::cout << "After copy:" << std::endl; 35 | bst.print(); 36 | std::cout << "------------------------------" << std::endl; 37 | } 38 | -------------------------------------------------------------------------------- /Sem_07/README.md: -------------------------------------------------------------------------------- 1 | # Двоично наредено дърво (Binary search tree) 2 | 3 | Искаме колекция, която поддържа следните операции: 4 | - добавяне на елемент 5 | - търсене на елемент 6 | - премахване на елемент 7 | 8 | **Двоично наредено дърво** 9 | Двоично дърво, при което: 10 | - Стойностите на всички върхове в ***лявото*** поддърво са ***по- малки*** от стойността на корена. 11 | - Стойностите на всички върхове в ***дясното*** поддърво са ***по- големи*** от стойността на корена. 12 | 13 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_07/images/bst.png) 14 | 15 | :question: n-тото ***число на Каталан*** е точно ***броят двоично наредени дървета с n върха***. 16 | 17 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_07/images/catalan.png) 18 | 19 | ***Добавяне на елемент (Insert)*** 20 | 21 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_07/images/insertInBst.png) 22 | 23 | ***Премахване на елемент (Remove)*** 24 | - Първи случай (Премахваме **листо**) 25 | 26 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_07/images/removeInBst1.png) 27 | 28 | - Втори случай (Премахваме **възел с едно дете**) 29 | 30 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_07/images/removeInBst2.png) 31 | 32 | - Трети случай (Премахваме **възел с две деца**) 33 | 34 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_07/images/removeInBst3.png) 35 | 36 | ***Сложност на операциите в средния случай:*** 37 | | |добавяне|търсене|премахване| 38 | |--|--|--|--| 39 | |Двоично наредено дърво|$\Theta(\log n)$|$\Theta(\log n)$|$\Theta(\log n)$| 40 | |Сортиран масив|$\Theta(n)$|$\Theta(\log n)$|$\Theta(n)$| 41 | 42 | --- 43 | 44 | ## Задачи 45 | 46 | **Задача 1:** Да се напише функция, която приема указател към корена на двоично наредено дърво и *създава сортиран масив от елементите му*. 47 | 48 | **Задача 2:** Да се напише функция, която приема указател към корена на двоично дърво и проверява *дали то е двоично дърво за търсене(BST)*. 49 | 50 | **Задача 3:** Нека е дадено двоично наредено дърво и два елемента, които се съдържат в дървото - x, y. Да се напише функция, която *намира LCA (най- малкия общ предшественик) на x и y*. 51 | 52 | **Задача 4:** *Huffman decoding*. 53 | https://www.hackerrank.com/challenges/tree-huffman-decoding/problem 54 | 55 | **Задача 5 (контролно):** Да се напише функция, която *намира сумата на всички елементи на двоично наредено дърво от цели числа, които имат нечетен брой наследници в интервала [x, y]* 56 | (т.е. сумата на броя елементи в лявото поддърво и броя елементи в дясното поддърво в интервала [x, y] е нечетно число). 57 | Нека x и y са параметри на функцията. 58 | 59 | **Задача 6 (контролно):** Да се напише функция, която приема указател към корена на двоично дърво и пресмята *сумата от елементите, които се намират на нечетно ниво и имат само десен наследник*. 60 | -------------------------------------------------------------------------------- /Sem_07/Task01.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) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | template 27 | void createSortedArrayFromBst(const Node* root, std::vector& v) 28 | { 29 | if (!root) 30 | { 31 | return; 32 | } 33 | createSortedArrayFromBst(root->left, v); 34 | v.push_back(root->data); 35 | createSortedArrayFromBst(root->right, v); 36 | } 37 | 38 | int main() 39 | { 40 | Node* root = new Node(50); 41 | Node* n1 = new Node(25); 42 | Node* n2 = new Node(75); 43 | Node* n3 = new Node(12); 44 | Node* n4 = new Node(30); 45 | Node* n5 = new Node(60); 46 | Node* n6 = new Node(85); 47 | Node* n7 = new Node(52); 48 | Node* n8 = new Node(70); 49 | 50 | root->left = n1; 51 | root->right = n2; 52 | n1->left = n3; 53 | n1->right = n4; 54 | n2->left = n5; 55 | n2->right = n6; 56 | n5->left = n7; 57 | n5->right = n8; 58 | 59 | std::vector v; 60 | createSortedArrayFromBst(root, v); 61 | for (size_t i = 0; i < v.size(); i++) 62 | { 63 | std::cout << v[i] << " "; 64 | } 65 | free(root); 66 | } 67 | -------------------------------------------------------------------------------- /Sem_07/Task02.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) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | template 27 | bool isBstRec(const Node* root, int min, int max) 28 | { 29 | if (!root) 30 | { 31 | return true; 32 | } 33 | if (root->data < min || root->data > max) 34 | { 35 | return false; 36 | } 37 | return isBstRec(root->left, min, root->data - 1) && isBstRec(root->right, root->data + 1, max); 38 | } 39 | 40 | template 41 | bool isBst(const Node* root) 42 | { 43 | return isBstRec(root, INT_MIN, INT_MAX); 44 | } 45 | 46 | int main() 47 | { 48 | Node* root = new Node(50); 49 | Node* n1 = new Node(25); 50 | Node* n2 = new Node(75); 51 | Node* n3 = new Node(12); 52 | Node* n4 = new Node(30); 53 | Node* n5 = new Node(60); 54 | Node* n6 = new Node(85); 55 | Node* n7 = new Node(52); 56 | Node* n8 = new Node(70); 57 | 58 | root->left = n1; 59 | root->right = n2; 60 | n1->left = n3; 61 | n1->right = n4; 62 | n2->left = n5; 63 | n2->right = n6; 64 | n5->left = n7; 65 | n5->right = n8; 66 | 67 | std::cout << isBst(root); 68 | free(root); 69 | } 70 | -------------------------------------------------------------------------------- /Sem_07/Task03.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) : data(data), left(nullptr), right(nullptr) {} 11 | }; 12 | 13 | template 14 | void free(Node* root) 15 | { 16 | if (!root) 17 | { 18 | return; 19 | } 20 | free(root->left); 21 | free(root->right); 22 | delete root; 23 | } 24 | 25 | template 26 | Node* lca(Node* root, const T& x, const T& y) 27 | { 28 | if ((x < root->data && y > root->data) || (y < root->data && x > root->data)) 29 | { 30 | return root; 31 | } 32 | if (x < root->data && y < root->data) 33 | { 34 | return lca(root->left, x, y); 35 | } 36 | return lca(root->right, x, y); 37 | } 38 | 39 | int main() 40 | { 41 | Node* root = new Node(50); 42 | Node* n1 = new Node(25); 43 | Node* n2 = new Node(75); 44 | Node* n3 = new Node(12); 45 | Node* n4 = new Node(30); 46 | Node* n5 = new Node(60); 47 | Node* n6 = new Node(85); 48 | Node* n7 = new Node(52); 49 | Node* n8 = new Node(70); 50 | 51 | root->left = n1; 52 | root->right = n2; 53 | n1->left = n3; 54 | n1->right = n4; 55 | n2->left = n5; 56 | n2->right = n6; 57 | n5->left = n7; 58 | n5->right = n8; 59 | 60 | Node* result = lca(root, 52, 85); 61 | std::cout << result->data; 62 | free(root); 63 | } 64 | -------------------------------------------------------------------------------- /Sem_07/Task04.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) : data(data), left(nullptr), right(nullptr) {} 12 | }; 13 | 14 | template 15 | void free(Node* root) 16 | { 17 | if (!root) 18 | { 19 | return; 20 | } 21 | free(root->left); 22 | free(root->right); 23 | delete root; 24 | } 25 | 26 | void getEncodedString(const Node* root, const std::string& coding, std::string& toGet, int& index) 27 | { 28 | if (!root->left && !root->right) 29 | { 30 | toGet.push_back(root->data); 31 | return; 32 | } 33 | if (coding[index] == '0') 34 | { 35 | getEncodedString(root->left, coding, toGet, ++index); 36 | } 37 | else 38 | { 39 | getEncodedString(root->right, coding, toGet, ++index); 40 | } 41 | } 42 | 43 | std::string readHuffmanEncoding(const Node* root, const std::string& coding) 44 | { 45 | std::string toGet = ""; 46 | int index = 0; 47 | while (coding[index] != '\0') 48 | { 49 | getEncodedString(root, coding, toGet, index); 50 | } 51 | return toGet; 52 | } 53 | 54 | int main() 55 | { 56 | Node* root = new Node('*'); 57 | Node* n1 = new Node('*'); 58 | Node* n2 = new Node('A'); 59 | Node* n3 = new Node('B'); 60 | Node* n4 = new Node('C'); 61 | 62 | root->left = n1; 63 | root->right = n2; 64 | n1->left = n3; 65 | n1->right = n4; 66 | 67 | std::string coding = "1001011"; 68 | std::cout << readHuffmanEncoding(root, coding); 69 | free(root); 70 | } 71 | -------------------------------------------------------------------------------- /Sem_07/Task05.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) : data(data), left(nullptr), right(nullptr) {} 11 | }; 12 | 13 | template 14 | void free(Node* root) 15 | { 16 | if (!root) 17 | { 18 | return; 19 | } 20 | free(root->left); 21 | free(root->right); 22 | delete root; 23 | } 24 | 25 | unsigned nodesCountInAnInterval(const Node* root, int start, int end) 26 | { 27 | if (!root) 28 | { 29 | return 0; 30 | } 31 | if (root->data >= start && root->data <= end) 32 | { 33 | return 1 + nodesCountInAnInterval(root->left, start, end) + nodesCountInAnInterval(root->right, start, end); 34 | } 35 | else if (root->data <= start) 36 | { 37 | return nodesCountInAnInterval(root->right, start, end); 38 | } 39 | else 40 | { 41 | return nodesCountInAnInterval(root->left, start, end); 42 | } 43 | } 44 | 45 | unsigned sumOfNodesWithOddNumberOfChildren(const Node* root, int start, int end) 46 | { 47 | if (!root) 48 | { 49 | return 0; 50 | } 51 | 52 | unsigned numOfChildren = nodesCountInAnInterval(root->left, start, end) + nodesCountInAnInterval(root->right, start, end); 53 | if (numOfChildren % 2) 54 | { 55 | return root->data + 56 | sumOfNodesWithOddNumberOfChildren(root->left, start, end) + sumOfNodesWithOddNumberOfChildren(root->right, start, end); 57 | } 58 | return sumOfNodesWithOddNumberOfChildren(root->left, start, end) + sumOfNodesWithOddNumberOfChildren(root->right, start, end); 59 | } 60 | 61 | int main() 62 | { 63 | Node* root = new Node(50); 64 | Node* n1 = new Node(25); 65 | Node* n2 = new Node(75); 66 | Node* n3 = new Node(12); 67 | Node* n4 = new Node(30); 68 | Node* n5 = new Node(60); 69 | Node* n6 = new Node(85); 70 | Node* n7 = new Node(52); 71 | Node* n8 = new Node(70); 72 | 73 | root->left = n1; 74 | root->right = n2; 75 | n1->left = n3; 76 | n1->right = n4; 77 | n2->left = n5; 78 | n2->right = n6; 79 | n5->left = n7; 80 | n5->right = n8; 81 | 82 | std::cout << sumOfNodesWithOddNumberOfChildren(root, 30, 75); 83 | free(root); 84 | } 85 | -------------------------------------------------------------------------------- /Sem_07/Task06.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) : data(data), left(nullptr), right(nullptr) {} 11 | }; 12 | 13 | template 14 | void free(Node* root) 15 | { 16 | if (!root) 17 | { 18 | return; 19 | } 20 | free(root->left); 21 | free(root->right); 22 | delete root; 23 | } 24 | 25 | int sumOfNodesOnOddLevelsHelper(const Node* root, unsigned level) 26 | { 27 | if (!root) 28 | { 29 | return 0; 30 | } 31 | if (level % 2) 32 | { 33 | if (root->right && !root->left) 34 | { 35 | return root->data; 36 | } 37 | } 38 | return sumOfNodesOnOddLevelsHelper(root->left, level + 1) + sumOfNodesOnOddLevelsHelper(root->right, level + 1); 39 | } 40 | 41 | int sumOfNodesOnOddLevels(const Node* root) 42 | { 43 | return sumOfNodesOnOddLevelsHelper(root, 0); 44 | } 45 | 46 | int main() 47 | { 48 | // Same tree as in the previous tasks but without the left child (12) of the node with value 25 49 | Node* root = new Node(50); 50 | Node* n1 = new Node(25); 51 | Node* n2 = new Node(75); 52 | Node* n3 = new Node(30); 53 | Node* n4 = new Node(60); 54 | Node* n5 = new Node(85); 55 | Node* n6 = new Node(52); 56 | Node* n7 = new Node(70); 57 | 58 | root->left = n1; 59 | root->right = n2; 60 | n1->right = n3; 61 | n2->left = n4; 62 | n2->right = n5; 63 | n4->left = n6; 64 | n4->right = n7; 65 | 66 | std::cout << sumOfNodesOnOddLevels(root); 67 | free(root); 68 | } 69 | -------------------------------------------------------------------------------- /Sem_07/images/bst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_07/images/bst.png -------------------------------------------------------------------------------- /Sem_07/images/catalan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_07/images/catalan.png -------------------------------------------------------------------------------- /Sem_07/images/insertInBst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_07/images/insertInBst.png -------------------------------------------------------------------------------- /Sem_07/images/removeInBst1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_07/images/removeInBst1.png -------------------------------------------------------------------------------- /Sem_07/images/removeInBst2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_07/images/removeInBst2.png -------------------------------------------------------------------------------- /Sem_07/images/removeInBst3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_07/images/removeInBst3.png -------------------------------------------------------------------------------- /Sem_08/AVL tree/AVL.md: -------------------------------------------------------------------------------- 1 | [AVL implementation](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/tree/main/AdvancedDS-AVL_DoS_Tree) 2 | -------------------------------------------------------------------------------- /Sem_08/README.md: -------------------------------------------------------------------------------- 1 | # Самобалансиращи се дървета 2 | 3 | Дървета, които се пренареждат така, че да поддържат логаритмична височина. 4 | 5 | # AVL дървета 6 | 7 | AVL дърво представлява самобалансиращо се двоично дърво за търсене (BST), при което разликата между височините на лявото и дясното поддърво за всеки възел не може да бъде повече от едно. Разликата между височините на лявото поддърво и дясното поддърво за даден възел се нарича коефициент на баланс на възела (balance factor). 8 | `bf(node) = height(tree->right) - height(tree->left)`; bf $\in \\{-1,0,1\\}$ 9 | 10 | Операциите за добавяне, търсене и премахване в AVL дърво са винаги със сложност $\Theta(\log n)$ в **най- лошия случай**. 11 | 12 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/avl.png) 13 | 14 | ## Ротации 15 | 16 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/rotation.png) 17 | 18 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/detailedRotation.png) 19 | 20 | ### `left left case` 21 | 22 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/leftLeftCase.png) 23 | 24 | ### `left right case` 25 | 26 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/leftRightCase.png) 27 | 28 | ### `right left case` 29 | 30 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/rightLeftCase.png) 31 | 32 | ### `right right case` 33 | 34 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_08/images/rightRightCase.png) 35 | -------------------------------------------------------------------------------- /Sem_08/images/avl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/avl.png -------------------------------------------------------------------------------- /Sem_08/images/detailedRotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/detailedRotation.png -------------------------------------------------------------------------------- /Sem_08/images/leftLeftCase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/leftLeftCase.png -------------------------------------------------------------------------------- /Sem_08/images/leftRightCase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/leftRightCase.png -------------------------------------------------------------------------------- /Sem_08/images/rightLeftCase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/rightLeftCase.png -------------------------------------------------------------------------------- /Sem_08/images/rightRightCase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/rightRightCase.png -------------------------------------------------------------------------------- /Sem_08/images/rotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_08/images/rotation.png -------------------------------------------------------------------------------- /Sem_09/Priority queue/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "priority_queue.hpp" 2 | #include 3 | 4 | int main() 5 | { 6 | priority_queue pq({ 3, 5, 22, 23, 24, 1, 1, 100 }); 7 | 8 | std::cout << pq.top() << std::endl; // 100 9 | pq.pop(); 10 | 11 | pq.push(-420); 12 | pq.push(-42); 13 | pq.push(55); 14 | 15 | std::cout << pq.top() << std::endl; // 55 16 | pq.pop(); 17 | std::cout << pq.top() << std::endl; // 24 18 | pq.pop(); 19 | std::cout << pq.top() << std::endl; // 23 20 | pq.pop(); 21 | std::cout << pq.top() << std::endl; // 22 22 | } 23 | -------------------------------------------------------------------------------- /Sem_09/Priority queue/priority_queue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include <> 5 | 6 | template 7 | class priority_queue 8 | { 9 | public: 10 | priority_queue() = default; 11 | priority_queue(const std::vector& v); 12 | 13 | const T& top() const; 14 | void push(const T& el); 15 | void pop(); 16 | 17 | size_t size() const; 18 | bool empty() const; 19 | 20 | private: 21 | static size_t leftChild(size_t i); 22 | static size_t rightChild(size_t i); 23 | static int parent(int i); 24 | 25 | void heapify(size_t ind); 26 | 27 | std::vector data; 28 | }; 29 | 30 | template 31 | priority_queue::priority_queue(const std::vector& v) // O(n) 32 | { 33 | data = v; 34 | for (int i = v.size() / 2 - 1; i >= 0; i--) 35 | { 36 | heapify(i); 37 | } 38 | } 39 | 40 | template 41 | const T& priority_queue::top() const 42 | { 43 | if (empty()) 44 | { 45 | throw std::runtime_error("Empty queue!"); 46 | } 47 | return data[0]; 48 | } 49 | 50 | template 51 | void priority_queue::push(const T& el) 52 | { 53 | data.push_back(el); 54 | int ind = data.size() - 1; 55 | int parentIndex = parent(ind); 56 | 57 | while (ind > 0 && data[ind] > data[parentIndex]) 58 | { 59 | std::swap(data[ind], data[parentIndex]); 60 | ind = parentIndex; 61 | parentIndex = parent(ind); 62 | } 63 | } 64 | 65 | template 66 | void priority_queue::pop() 67 | { 68 | if (empty()) 69 | { 70 | throw std::runtime_error("Empty queue!"); 71 | } 72 | 73 | data[0] = data[data.size() - 1]; 74 | data.pop_back(); 75 | if (data.size() != 0) 76 | { 77 | heapify(0); 78 | } 79 | } 80 | 81 | template 82 | size_t priority_queue::size() const 83 | { 84 | return data.size(); 85 | } 86 | 87 | template 88 | bool priority_queue::empty() const 89 | { 90 | return data.empty(); 91 | } 92 | 93 | template 94 | size_t priority_queue::leftChild(size_t i) 95 | { 96 | return 2 * i + 1; 97 | } 98 | 99 | template 100 | size_t priority_queue::rightChild(size_t i) 101 | { 102 | return 2 * i + 2; 103 | } 104 | 105 | template 106 | int priority_queue::parent(int i) 107 | { 108 | return (i - 1) / 2; 109 | } 110 | 111 | template 112 | void priority_queue::heapify(size_t ind) 113 | { 114 | int currentElementIndex = ind; 115 | while (true) 116 | { 117 | int leftChildIndex = leftChild(currentElementIndex); 118 | int rightChildIndex = rightChild(currentElementIndex); 119 | 120 | bool shouldGoLeft = leftChildIndex < data.size() && data[leftChildIndex] > data[currentElementIndex]; 121 | bool shouldGoRight = rightChildIndex < data.size() && data[rightChildIndex] > data[currentElementIndex]; 122 | 123 | if (shouldGoLeft && shouldGoRight) 124 | { 125 | if (data[leftChildIndex] > data[rightChildIndex]) 126 | { 127 | std::swap(data[currentElementIndex], data[leftChildIndex]); 128 | currentElementIndex = leftChildIndex; 129 | } 130 | else 131 | { 132 | std::swap(data[currentElementIndex], data[rightChildIndex]); 133 | currentElementIndex = rightChildIndex; 134 | } 135 | } 136 | else if (shouldGoLeft) 137 | { 138 | std::swap(data[currentElementIndex], data[leftChildIndex]); 139 | currentElementIndex = leftChildIndex; 140 | } 141 | else if (shouldGoRight) 142 | { 143 | std::swap(data[currentElementIndex], data[rightChildIndex]); 144 | currentElementIndex = rightChildIndex; 145 | } 146 | else 147 | { 148 | break; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Sem_09/README.md: -------------------------------------------------------------------------------- 1 | # Двоична пирамида. Приоритетна опашка 2 | 3 | ## Двоична пирамида (Binary heap) 4 | 5 | ***Дефиниция:*** **Попълнено двоично дърво (Complete binary tree)** е наредено двоично дърво, в което: 6 | - Всички нива, с изключение, може би, на последното, имат максималния възможен брой върхове. 7 | - Ако листата са на две нива, листата на последното ниво са максимално вляво. 8 | 9 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_09/images/CompleteBinaryTree.png) 10 | 11 | ***Дефиниция:*** **Двоична пирамида (Binary heap)** е попълнено двоично дърво, в което: 12 | - Или ключът на всеки връх е *по-голям или равен* на ключовете на неговите деца и тогава пирамидата е _**максимална пирамида (max heap)**_, 13 | - или ключът на всеки връх е *по-малък или равен* на ключовете на неговите деца и тогава пирамидата е _**минимална пирамида (min heap)**_. 14 | 15 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_09/images/BinaryHeap.png) 16 | 17 | ## Реализация на двоична пирамида 18 | 19 | Ще реализираме двоична пирамида *чрез масив*, който ще наричаме ***линеаризация*** на пирамидата. 20 | Линеаризирането можем да си представяме като обхождане на дървото (пирамида) по нива отляво надясно и последователно записване на ключовете в масива. 21 | Например, линеаризация на пирамидата от горната картинка е масивът **[10, 8, 9, 3, 2, 1]**. 22 | 23 | Тъй като дървото е попълнено, в масива няма как да има "дупки". 24 | ***Предимството на тази реализация е, че се избягва разхода на памет.*** Ако искаме да представим двоично дърво в общия случай, не непременно попълнено, трябва по някакъв начин да укажем за всеки връх кой е родителят или кои са децата. Това ни струва допълнителна памет. 25 | 26 | Нека T е пирамида с n елемента и масивът A[0,..,n-1] е нейната линеаризация. Тогава за всеки елемент A[i]: 27 | - Елементът, който отговаря на **родителя на A[i], e A[(i - 1) / 2]**, ако върхът на T, който отговаря на A[i], не е коренът. 28 | - Елементът, който отговаря на **лявото дете на A[i], e A[2\*i + 1]**, ако върхът на T, отговарящ на A[i], има ляво дете. 29 | - Елементът, който отговаря на **дясното дете на A[i], e A[2\*i + 2]**, ако върхът на T, отговарящ на A[i], има дясно дете. 30 | 31 | Нека A[0,..,n-1] е масив от ключове. 32 | ***Пирамидална инверсия*** в A наричаме всяка наредена двойка индекси , такива че i < j, A[i] е предшественик на A[j] и A[i] < A[j]. 33 | ***Масивът от ключове A е пирамида тогава и само тогава, когато няма нито една пирамидална инверсия.*** 34 | 35 | ## Бързо построяване на пирамида - алгоритъм Build Heap 36 | 37 | В същината на алгоритъма Build Heap "стои" функцията **heapify**. 38 | 39 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_09/images/heapify.png) 40 | 41 | ### Реализация на heapify 42 | 43 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_09/images/heapify-1.png) 44 | 45 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_09/images/heapify-2.png) 46 | 47 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_09/images/heapify-3.png) 48 | 49 | ***Сложност*** - $\Theta(\log n)$ 50 | 51 | ### Реализация на Build Heap 52 | 53 | ```c++ 54 | BuildHeap(const std::vector& v) 55 | { 56 | for (size_t i = v.size() / 2 - 1; i >= 0; i--) 57 | { 58 | heapify(i); 59 | } 60 | } 61 | ``` 62 | 63 | **Сложност** - $\Theta(n)$ 64 | 65 | --- 66 | 67 | ## Приоритетна опашка (Priority queue) 68 | 69 | Приоритетната опашка е абстрактна структура от данни (ADT), която очакваме да има следните функции в интерфейса си: 70 | - **top** - връща елемента с най- висок приоритет - $\Theta(1)$ 71 | - **push** - вмъква елемент - $\Theta(\log n)$ 72 | - **pop** - премахва елемента с най- висок приоритет - $\Theta(\log n)$ 73 | 74 | :bangbang: **Забележка** :bangbang: 75 | Ако в приоритетната опашка имаме елементи с еднакви ключове, **тя трябва да има поведението на "нормална" опашка (FIFO).** 76 | -------------------------------------------------------------------------------- /Sem_09/heapSort.cpp: -------------------------------------------------------------------------------- 1 | #include "PriorityQueue/priority_queue.hpp" 2 | #include 3 | #include 4 | 5 | template 6 | void heapSort(std::vector& v) // O(nlog(n)) 7 | { 8 | priority_queue pq(v); // O(n) 9 | for (int i = v.size() - 1; i >= 0; i--) // O(n) 10 | { 11 | v[i] = pq.top(); // O(log(n)) 12 | pq.pop(); 13 | } 14 | } 15 | 16 | int main() 17 | { 18 | std::vector v{ 10, 3, 0, 88, 101, 20 }; 19 | heapSort(v); 20 | for (size_t i = 0; i < v.size(); i++) 21 | { 22 | std::cout << v[i] << " "; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sem_09/images/BinaryHeap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_09/images/BinaryHeap.png -------------------------------------------------------------------------------- /Sem_09/images/CompleteBinaryTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_09/images/CompleteBinaryTree.png -------------------------------------------------------------------------------- /Sem_09/images/heapify-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_09/images/heapify-1.png -------------------------------------------------------------------------------- /Sem_09/images/heapify-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_09/images/heapify-2.png -------------------------------------------------------------------------------- /Sem_09/images/heapify-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_09/images/heapify-3.png -------------------------------------------------------------------------------- /Sem_09/images/heapify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_09/images/heapify.png -------------------------------------------------------------------------------- /Sem_10/README.md: -------------------------------------------------------------------------------- 1 | # Хеш таблици 2 | 3 | ## Въведение 4 | Хеш таблицата е структура от данни, която се състои от наредени двойки **<ключ (key), стойност (value)>** или **само от ключове** (keys). 5 | При хеш таблицата имаме **хешираща функция**, която при подаден ключ генерира индекс **(хеш код/hash code)**, на който трябва да се запише съответната стойност (value). По принцип, този индекс е уникален. Може, обаче, да се случи генериране на един и същ хеш код за два различни ключа, което довежда до дублиране на индекси. Този проблем е познат като **колизия (collision)**. 6 | 7 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_10/images/Hash.jpeg) 8 | 9 | ## Хеш функции 10 | - Функцията трябва да е детерминистична, т.е. ако $x = y$, то $hash(x) = hash(y)$. 11 | - Трябва да използва всички данни от key. 12 | - За "подобни" ключове, функцията връща "напълно различни" индекси. 13 | - Равномерна функция (броят на колизиите на всеки индекс е относително еднакъв). 14 | - Бърза за изчислване. :exclamation: 15 | 16 | **"Перфектна" хеш функция - никога няма колизии!** 17 | В реалния свят такава функция рядко е възможно да бъде създадена (възможните слотове обикновено са значително по- малко от възможните ключове). 18 | Например, ако множеството от ключовете са стрингове с дължина $20$, съставени от буквите на английската азбука, то възможните стрингове са $26^{20}$, а възможните слотове - около $4\*10^{12}$. Следователно е невъзможно да нямаме колизия, ако хешираме повече от $4\*10^{12}$ елемента. 19 | 20 | ## Хеш таблици 21 | ### Основни операции: 22 | - insert(x) - $\Theta(1)$ :heavy_check_mark: 23 | - find(x) - $\Theta(1)$ :heavy_check_mark: 24 | - remove(x) - $\Theta(1)$ :heavy_check_mark: 25 | 26 | ### Видове: 27 | - с ключ и стойност (**map**) 28 | - само ключ (**set**) 29 | 30 | ### Недостатъци: 31 | - няма наредба 32 | - сложност на хеш-функцията 33 | - операциите не винаги са константни 34 | - преразход на памет 35 | - неудобно обхождане 36 | 37 | ## Collision resolution strategies (CRS) 38 | ### 1. `Външно хеширане/Затворено адресиране` (External Hashing/Closed addressing/Separate Chaining) - данните са извън таблицата 39 | Таблицата е масив от „кофи“ (buckets). 40 | Всяка кофа е отделен контейнер (най-често списък). 41 | Преоразмеряване на таблицата при запълване над 70% чрез прехеширане на всички елементи. 42 | 43 | :question: Нарича се ***затворено адресиране***, защото винаги $x \in table[h(x)]$. 44 | 45 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_10/images/OpenHashing.png) 46 | 47 | :heavy_check_mark: Общ брой елементи, по- голям от размера на таблицата, е ок. 48 | :heavy_check_mark: Лесно намираме всички елементи с даден ключ. 49 | :heavy_check_mark: Изтриването е лесно. 50 | :x: Индиректен достъп до елементите. 51 | :x: Повече памет. 52 | 53 | ### 2. `Вътрешно хеширане/Отворено адресиране` (Closed Hashing/Open addressing) - данните са в таблицата 54 | Таблицата съдържа всички елементи. 55 | За всяка клетка има допълнителни два флага – *empty* и *deleted*. 56 | 57 | ### Справяне с колизии 58 | - **Линейно пробване** - Стъпката S, която избираме, трябва да бъде взаимно проста с размера n на таблицата! **$(S, n) = 1$** :exclamation: 59 | - **Квадратично пробване** 60 | - **Двойно хеширане** 61 | 62 | Други стратегии – **Ку-Ку хеширане** 63 | 64 | При вътрешното хеширане sizeof(table), n > броя елементи, cnt! :exclamation: 65 | Препоръка: $n > 1,5\*cnt$. 66 | 67 | ![alt text](https://github.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/blob/main/Sem_10/images/ClosedHashing.png) 68 | 69 | :heavy_check_mark: По- малко памет. 70 | :heavy_check_mark: Директен достъп по index. 71 | :x: По- трудно изтриване. 72 | :x: Неконсистентни позиции. 73 | 74 | --- 75 | 76 | ## Задачи 77 | 78 | [1. Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/description/) 79 | [2. 0-1 Subarray](https://www.hackerrank.com/contests/sda-homework-9/challenges/0-1-1) 80 | [3. Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/description/) 81 | -------------------------------------------------------------------------------- /Sem_10/images/ClosedHashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_10/images/ClosedHashing.png -------------------------------------------------------------------------------- /Sem_10/images/Hash.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_10/images/Hash.jpeg -------------------------------------------------------------------------------- /Sem_10/images/OpenHashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MariaGrozdeva/Data_structures_and_algorithms_FMI/fa71c1c8183561ca6e0fe0c0ee1617af7f6e5bd8/Sem_10/images/OpenHashing.png -------------------------------------------------------------------------------- /Sem_10/subarraySumK.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int subarraySum(const std::vector& v, int k) 6 | { 7 | int sum = 0; 8 | size_t count = 0; 9 | std::unordered_map map; 10 | 11 | for (size_t i = 0; i < v.size(); i++) 12 | { 13 | sum += v[i]; 14 | if (sum == k) 15 | { 16 | count++; 17 | } 18 | if (map.find(sum - k) != map.end()) 19 | { 20 | count += map[sum - k]; 21 | } 22 | map[sum]++; 23 | } 24 | 25 | return count; 26 | } 27 | 28 | int main() 29 | { 30 | std::vector v{ 4, 2, 2 }; 31 | std::cout << subarraySum(v, 4); 32 | } 33 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/UnweightedGraph/GraphAlgorithms.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphAlgorithms.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | Graph::Graph(size_t verticesCount, bool oriented) : adjList(verticesCount), oriented(oriented) 9 | {} 10 | 11 | void Graph::addEdge(unsigned start, unsigned end) 12 | { 13 | if (start >= adjList.size() || end >= adjList.size()) 14 | { 15 | return; 16 | } 17 | adjList[start].push_back(end); 18 | if (!oriented) 19 | { 20 | adjList[end].push_back(start); 21 | } 22 | } 23 | 24 | void Graph::bfs(unsigned start) const 25 | { 26 | if (start >= adjList.size()) 27 | { 28 | return; 29 | } 30 | 31 | std::vector visited(adjList.size(), false); 32 | std::queue q; 33 | 34 | q.push(start); 35 | visited[start] = true; 36 | 37 | while (!q.empty()) 38 | { 39 | unsigned currVertex = q.front(); 40 | q.pop(); 41 | 42 | for (unsigned neighbor : adjList[currVertex]) 43 | { 44 | if (visited[neighbor]) 45 | { 46 | continue; 47 | } 48 | std::cout << neighbor << std::endl; 49 | q.push(neighbor); 50 | visited[neighbor] = true; 51 | } 52 | } 53 | } 54 | 55 | void Graph::bfsShortestPaths(unsigned start) const 56 | { 57 | if (start >= adjList.size()) 58 | { 59 | return; 60 | } 61 | std::vector visited(adjList.size(), false); 62 | std::queue q; 63 | 64 | q.push(start); 65 | visited[start] = true; 66 | 67 | unsigned dist = 0; 68 | 69 | while (!q.empty()) 70 | { 71 | const size_t currentLevelSize = q.size(); 72 | for (size_t i = 0; i < currentLevelSize; i++) 73 | { 74 | unsigned currVertex = q.front(); 75 | q.pop(); 76 | 77 | for (unsigned neighbor : adjList[currVertex]) 78 | { 79 | if (visited[neighbor]) 80 | { 81 | continue; 82 | } 83 | std::cout << "Vertex: " << currVertex << ", path length: " << dist << std::endl; 84 | q.push(neighbor); 85 | visited[neighbor] = true; 86 | } 87 | } 88 | dist++; 89 | } 90 | } 91 | 92 | void Graph::dfsIter(unsigned start) const 93 | { 94 | if (start >= adjList.size()) 95 | { 96 | return; 97 | } 98 | std::vector visited(adjList.size(), false); 99 | std::stack s; 100 | 101 | s.push(start); 102 | 103 | while (!s.empty()) 104 | { 105 | unsigned currVertex = s.top(); 106 | s.pop(); 107 | 108 | if (visited[currVertex]) 109 | { 110 | continue; 111 | } 112 | visited[currVertex] = true; 113 | 114 | std::cout << "Vertex: " << currVertex << std::endl; 115 | 116 | for (unsigned neighbor : adjList[currVertex]) 117 | { 118 | s.push(neighbor); 119 | } 120 | } 121 | } 122 | 123 | void Graph::dfsRec(unsigned start) const 124 | { 125 | if (start >= adjList.size()) 126 | { 127 | return; 128 | } 129 | std::vector visited(adjList.size(), false); 130 | dfsRec(start, visited); 131 | } 132 | 133 | void Graph::dfsRec(unsigned currVertex, std::vector& visited) const 134 | { 135 | visited[currVertex] = true; 136 | std::cout << "Vertex: " << currVertex << std::endl; 137 | for (unsigned neighbor : adjList[currVertex]) 138 | { 139 | if (!visited[neighbor]) 140 | { 141 | dfsRec(neighbor, visited); 142 | } 143 | } 144 | } 145 | 146 | bool Graph::containsCycle() const 147 | { 148 | std::vector visited(adjList.size(), false); 149 | std::vector inStack(adjList.size(), false); 150 | 151 | for (size_t i = 0; i < adjList.size(); i++) 152 | { 153 | if (!visited[i] && containsCycleRec(i, visited, inStack)) 154 | { 155 | return true; 156 | } 157 | } 158 | return false; 159 | } 160 | 161 | bool Graph::containsCycleRec(unsigned currVertex, std::vector& visited, std::vector& s) const 162 | { 163 | visited[currVertex] = true; 164 | s[currVertex] = true; 165 | 166 | for (unsigned neighbor : adjList[currVertex]) 167 | { 168 | if (s[neighbor]) 169 | { 170 | return true; 171 | } 172 | if (!visited[neighbor] && containsCycleRec(neighbor, visited, s)) 173 | { 174 | return true; 175 | } 176 | } 177 | s[currVertex] = false; 178 | return false; 179 | } 180 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/UnweightedGraph/GraphAlgorithms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Graph 9 | { 10 | public: 11 | Graph(size_t verticesCount, bool oriented); 12 | void addEdge(unsigned start, unsigned end); 13 | 14 | void bfs(unsigned start) const; 15 | 16 | void bfsShortestPaths(unsigned start) const; 17 | 18 | void dfsIter(unsigned start) const; 19 | void dfsRec(unsigned start) const; 20 | 21 | bool containsCycle() const; 22 | 23 | private: 24 | void dfsRec(unsigned currVertex, std::vector& visited) const; 25 | bool containsCycleRec(unsigned currVertex, std::vector& visited, std::vector& s) const; 26 | 27 | std::vector> adjList; 28 | bool oriented; 29 | }; 30 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/UnweightedGraph/main.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphAlgorithms.h" 2 | #include 3 | 4 | int main() 5 | { 6 | Graph g(9, false); 7 | g.addEdge(0, 1); 8 | g.addEdge(0, 7); 9 | g.addEdge(1, 7); 10 | g.addEdge(1, 2); 11 | g.addEdge(7, 8); 12 | g.addEdge(7, 6); 13 | g.addEdge(8, 6); 14 | g.addEdge(8, 2); 15 | g.addEdge(2, 5); 16 | g.addEdge(6, 5); 17 | g.addEdge(2, 3); 18 | g.addEdge(3, 5); 19 | g.addEdge(3, 4); 20 | g.addEdge(5, 4); 21 | 22 | g.bfs(0); 23 | } 24 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/WeightedGraph/GraphAlgorithms.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphAlgorithms.h" 2 | #include "UnionFind/UnionFind.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | WeightedGraph::WeightedGraph(size_t verticesCount, bool oriented) : adjList(verticesCount), oriented(oriented) 10 | {} 11 | 12 | void WeightedGraph::addEdge(unsigned start, unsigned end, int weight) 13 | { 14 | if (start >= adjList.size() || end >= adjList.size()) 15 | { 16 | return; 17 | } 18 | adjList[start].push_back(std::make_pair(end, weight)); 19 | if (!oriented) 20 | { 21 | adjList[end].push_back(std::make_pair(start, weight)); 22 | } 23 | } 24 | 25 | void WeightedGraph::dijkstra(unsigned start, std::vector& distances) const 26 | { 27 | distances.resize(adjList.size(), UINT_MAX); 28 | distances[start] = 0; 29 | 30 | auto comparator = [&distances](const unsigned& lhs, const unsigned& rhs) { 31 | return distances[lhs] > distances[rhs]; 32 | }; 33 | std::priority_queue, decltype(comparator)> q(comparator); 34 | 35 | q.push(start); 36 | 37 | while (!q.empty()) 38 | { 39 | unsigned currVertex = q.top(); 40 | q.pop(); 41 | 42 | for (const auto& [currNeighbor, currEdgeWeight] : adjList[currVertex]) 43 | { 44 | if (distances[currNeighbor] > distances[currVertex] + currEdgeWeight) 45 | { 46 | distances[currNeighbor] = distances[currVertex] + currEdgeWeight; 47 | q.push(currNeighbor); 48 | } 49 | } 50 | } 51 | } 52 | 53 | void WeightedGraph::bellmanFord(unsigned start, std::vector& distances) const 54 | { 55 | distances.resize(adjList.size(), INT_MAX); 56 | distances[start] = 0; 57 | 58 | for (size_t phase = 0; phase < adjList.size(); phase++) 59 | { 60 | bool isLastPhase = phase == adjList.size() - 1; 61 | 62 | for (size_t i = 0; i < adjList.size(); i++) 63 | { 64 | if (distances[i] == INT_MAX) 65 | { 66 | continue; 67 | } 68 | 69 | unsigned currVertex = i; 70 | for (size_t j = 0; j < adjList[currVertex].size(); j++) 71 | { 72 | unsigned currNeighbor = adjList[currVertex][j].first; 73 | int currEdgeWeight = adjList[currVertex][j].second; 74 | if (distances[currNeighbor] > distances[currVertex] + currEdgeWeight) 75 | { 76 | if (isLastPhase) 77 | { 78 | distances[currNeighbor] = INT_MAX; 79 | throw std::logic_error("Negative cycle detected!"); 80 | } 81 | distances[currNeighbor] = distances[currVertex] + currEdgeWeight; 82 | } 83 | } 84 | } 85 | } 86 | } 87 | 88 | int WeightedGraph::prim(std::vector& mst) const 89 | { 90 | int mstWeight = 0; 91 | size_t addedEdges = 0; 92 | 93 | std::vector visited(adjList.size(), false); 94 | 95 | auto comp = [](const Edge& lhs, const Edge& rhs) { return std::get<2>(lhs) > std::get<2>(rhs); }; 96 | std::priority_queue, decltype(comp)> q(comp); 97 | 98 | q.push(std::make_tuple(0, 0, 0)); // virtual edge for the start 99 | 100 | while (addedEdges < adjList.size() - 1) 101 | { 102 | auto currentEdge = q.top(); 103 | q.pop(); 104 | 105 | unsigned currentEdgeStart = std::get<0>(currentEdge); 106 | unsigned currentEdgeEnd = std::get<1>(currentEdge); 107 | int currentEdgeWeight = std::get<2>(currentEdge); 108 | 109 | if (visited[currentEdgeEnd]) 110 | { 111 | continue; 112 | } 113 | 114 | mstWeight += currentEdgeWeight; 115 | 116 | if (currentEdgeStart != currentEdgeEnd) // in order not to count the virtual edge 117 | { 118 | addedEdges++; 119 | mst.emplace_back(currentEdgeStart, currentEdgeEnd, currentEdgeWeight); 120 | } 121 | visited[currentEdgeEnd] = true; 122 | 123 | for (const auto& [currNeighbor, currEdgeWeight] : adjList[currentEdgeEnd]) 124 | { 125 | q.push({ currentEdgeEnd, currNeighbor, currEdgeWeight }); 126 | } 127 | } 128 | return mstWeight; 129 | } 130 | 131 | int WeightedGraph::kruskal(std::vector& mst) const 132 | { 133 | int mstWeight = 0; 134 | size_t addedEdges = 0; 135 | 136 | std::vector edges; 137 | for (size_t i = 0; i < adjList.size(); ++i) 138 | { 139 | for (const auto& [neighbor, weight] : adjList[i]) 140 | { 141 | edges.emplace_back(i, neighbor, weight); 142 | } 143 | } 144 | std::sort(edges.begin(), edges.end(), [](const Edge& lhs, const Edge& rhs) { return std::get<2>(lhs) < std::get<2>(rhs); }); 145 | 146 | UnionFind uf(adjList.size()); 147 | 148 | for (size_t i = 0; addedEdges < adjList.size() - 1; i++) 149 | { 150 | auto currentEdge = edges[i]; 151 | 152 | unsigned currentEdgeStart = std::get<0>(currentEdge); 153 | unsigned currentEdgeEnd = std::get<1>(currentEdge); 154 | int currentEdgeWeight = std::get<2>(currentEdge); 155 | 156 | if (!uf.Union(currentEdgeStart, currentEdgeEnd)) // If edge "start---end" is added, it will form a cycle 157 | { 158 | continue; 159 | } 160 | 161 | mst.emplace_back(currentEdgeStart, currentEdgeEnd, currentEdgeWeight); 162 | mstWeight += currentEdgeWeight; 163 | addedEdges++; 164 | } 165 | 166 | return mstWeight; 167 | } 168 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/WeightedGraph/GraphAlgorithms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using Edge = std::tuple; 10 | 11 | class WeightedGraph 12 | { 13 | public: 14 | WeightedGraph(size_t verticesCount, bool oriented); 15 | void addEdge(unsigned start, unsigned end, int weight); 16 | 17 | void dijkstra(unsigned start, std::vector& distances) const; 18 | 19 | void bellmanFord(unsigned start, std::vector& distances) const; 20 | 21 | int prim(std::vector& mst) const; 22 | 23 | int kruskal(std::vector& mst) const; 24 | 25 | private: 26 | std::vector>> adjList; 27 | bool oriented; 28 | }; 29 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/WeightedGraph/UnionFind/UnionFind.cpp: -------------------------------------------------------------------------------- 1 | #include "UnionFind.h" 2 | 3 | UnionFind::UnionFind(unsigned n) : parents(n), sizes(n) 4 | { 5 | for (size_t i = 0; i < n; i++) 6 | { 7 | parents[i] = i; // At the beginning each set consists of only one element which is also a parent to itself (and the leader of the set) 8 | sizes[i] = 1; // All sets are with one element 9 | } 10 | } 11 | 12 | bool UnionFind::Union(unsigned n, unsigned k) // Union by rank 13 | { 14 | unsigned firstLeader = Find(n); 15 | unsigned secondLeader = Find(k); 16 | 17 | if (firstLeader == secondLeader) // n and k are already in one set 18 | { 19 | return false; 20 | } 21 | if (sizes[firstLeader] < sizes[secondLeader]) 22 | { 23 | std::swap(firstLeader, secondLeader); 24 | } 25 | 26 | // The set with leader "firstLeader" certainly has more elements than the other one 27 | parents[secondLeader] = firstLeader; 28 | sizes[firstLeader] += sizes[secondLeader]; 29 | 30 | return true; 31 | } 32 | 33 | unsigned UnionFind::Find(unsigned n) // Returns the leader of the set where n belongs to 34 | { 35 | if (parents[n] == n) 36 | { 37 | return n; 38 | } 39 | return Find(parents[n]); 40 | } 41 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/WeightedGraph/UnionFind/UnionFind.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class UnionFind 5 | { 6 | public: 7 | UnionFind(unsigned n); // set of n elements (0..n-1) 8 | 9 | bool Union(unsigned n, unsigned k); // O(log(n)) 10 | size_t Find(unsigned n); // O(log(n)) 11 | 12 | private: 13 | std::vector parents; 14 | std::vector sizes; 15 | }; 16 | -------------------------------------------------------------------------------- /Sem_11/Graph algorithms/WeightedGraph/main.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphAlgorithms.h" 2 | #include 3 | 4 | int main() 5 | { 6 | { // Dijkstra 7 | WeightedGraph g(9, false); 8 | g.addEdge(0, 1, 4); 9 | g.addEdge(0, 7, 8); 10 | g.addEdge(1, 7, 11); 11 | g.addEdge(1, 2, 8); 12 | g.addEdge(7, 8, 7); 13 | g.addEdge(7, 6, 1); 14 | g.addEdge(8, 6, 6); 15 | g.addEdge(8, 2, 2); 16 | g.addEdge(2, 5, 4); 17 | g.addEdge(6, 5, 2); 18 | g.addEdge(2, 3, 7); 19 | g.addEdge(3, 5, 14); 20 | g.addEdge(3, 4, 9); 21 | g.addEdge(5, 4, 10); 22 | 23 | std::vector distances; 24 | g.dijkstra(0, distances); 25 | 26 | for (size_t i = 0; i < distances.size(); i++) 27 | { 28 | std::cout << "Distance to vertex " << i << ": " << distances[i] << std::endl; 29 | } 30 | } 31 | 32 | std::cout << "--------------------------------------------------------------------------------" << std::endl; 33 | 34 | { // Bellman-Ford 35 | WeightedGraph g(6, true); 36 | g.addEdge(0, 1, 20); 37 | g.addEdge(0, 2, 10); 38 | g.addEdge(1, 3, 33); 39 | g.addEdge(1, 4, 20); 40 | g.addEdge(2, 3, 10); 41 | g.addEdge(2, 4, 50); 42 | g.addEdge(4, 3, -20); 43 | g.addEdge(3, 5, 1); 44 | g.addEdge(4, 5, -2); 45 | 46 | std::vector distances; 47 | g.bellmanFord(0, distances); 48 | 49 | for (size_t i = 0; i < distances.size(); i++) 50 | { 51 | std::cout << "Distance to vertex " << i << ": " << distances[i] << std::endl; 52 | } 53 | } 54 | 55 | std::cout << "--------------------------------------------------------------------------------" << std::endl; 56 | 57 | { // Prim's MST 58 | WeightedGraph g(6, false); 59 | g.addEdge(0, 1, 4); 60 | g.addEdge(0, 2, 4); 61 | g.addEdge(1, 2, 2); 62 | g.addEdge(1, 3, 5); 63 | g.addEdge(2, 3, 8); 64 | g.addEdge(2, 4, 10); 65 | g.addEdge(3, 4, 2); 66 | g.addEdge(3, 5, 6); 67 | g.addEdge(4, 5, 3); 68 | 69 | std::vector mst; 70 | int mstWeight = g.prim(mst); 71 | 72 | std::cout << "Minimum Spanning Tree (Prim's Algorithm):\n"; 73 | std::cout << "Total MST weight: " << mstWeight << std::endl; 74 | for (const auto& [u, v, w] : mst) 75 | { 76 | std::cout << "Edge: " << u << " - " << v << " (Weight: " << w << ")\n"; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sem_11/README.md: -------------------------------------------------------------------------------- 1 | . 2 | --------------------------------------------------------------------------------