├── README.md ├── lab1_queue.cpp ├── lab2_comparators.cpp ├── lab3_counting_sort.cpp ├── lab3_partition.cpp ├── lab4_hashtable.cpp ├── lab5_avltree.cpp ├── lab5_btree.cpp ├── lab6_bitwriter.cpp ├── lab7_listgraph.cpp ├── lab8_8game.cpp └── lab9_dsu.cpp /README.md: -------------------------------------------------------------------------------- 1 | Код с семинаров группы WEB/ML-13 2 | 3 | Семинар 1 4 | --------- 5 | Запись: https://www.youtube.com/watch?v=zFw1yF_rTQs 6 | 7 | **lab1_queue.cpp** 8 | 9 | Реализация очереди на односвязных списках. Обращаю внимание, что в ДЗ очередь должна быть реализована на динамическом зацикленном массиве (в варианте 3 -- на стеках, которые в свою очередь реализованы на динамическом массиве). Это значит, что: 10 | 1) В процессе работы массив должен уметь расширяться. Нельзя предсоздавать массив гигантского размера, в который гарантированно влезет весь ввод по условию. 11 | 2) Расширение массива должно происходить только тогда, когда пустых ячеек в массиве не осталось. Например, если при дописывании в конец достигли последней ячейки массива, нужно попытаться перепрыгнуть в первую и продолжить запись оттуда. 12 | 13 | Иллюстрация: 14 | - Картинка б) получилась из а) после добавления в конец очереди элементов 17, 3, 5. 15 | - Картинка в) получилась из б) после извлечения одного элемента из головы очереди. 16 | 17 | ![Зацикленный буфер](https://user-images.githubusercontent.com/1894130/194703133-14d6ab57-de4f-4cba-b15d-0bdfe4145b10.png) 18 | 19 | Семинар 2 20 | --------- 21 | Запись: https://www.youtube.com/watch?v=zFw1yF_rTQs 22 | 23 | **lab2_comparators.cpp** 24 | 25 | Пример пробрасывания компаратора в функцию. В mySort для этой цели добавлен параметр cmp типа "указатель на функцию". В mySort2 мы пошли еще дальше, ввели дополнительный шаблонный тип Comparator, позволяющий пробросить четвертым аргументом что угодно, что можно "вызывать". 26 | 27 | При реализации класса Heap компаратор должен храниться внутри кучи, инициализироваться в момент создания кучи. Не надо пробрасывать компаратор при вызове каждого метода. 28 | 29 | Семинар 3 30 | --------- 31 | Запись: https://www.youtube.com/watch?v=szVpkhFjLXw 32 | 33 | **lab3_partition.cpp** 34 | 35 | Пример реализации функции partition, где в качестве опорного элемента берется самый левый в массиве, итераторы идут навстречу друг другу. В домашнем задании запрещено использование рекурсии, итераторы сонаправлены, выбор опорного элемента согласно варианту. 36 | 37 | **lab3_counting_sort.cpp** 38 | 39 | Реализация сортировки подсчетом. 40 | 41 | Семинар 4 42 | --------- 43 | Запись: https://www.youtube.com/watch?v=sadmpgW8UYY 44 | 45 | **lab4_hashtable.cpp** 46 | 47 | Реализация хэш-таблицы с разрешением коллизий методом цепочек. В домашних работах нужно использовать метод открытой адресации. 48 | 49 | Семинар 5 50 | --------- 51 | Запись: https://www.youtube.com/watch?v=1p4TF5XVByE 52 | 53 | **lab5_avltree.cpp** 54 | 55 | Реализация AVL дерева. Необходимо его доработать: 56 | - добавить компаратор 57 | - логику findMin и removeMin нужно объединить в один метод findAndRemoveMin 58 | - элемент на место удаляемого берется из более глубокого поддерева (либо максимальный из левого, либо минимальный из правого) 59 | 60 | **lab5_btree.cpp** 61 | 62 | Частичная реализация B-дерева. Для полноценной вставки не хватает метода splitChild, его нужно дописать самому (на слайдах есть псевдокод). 63 | 64 | Семинар 6 65 | --------- 66 | Запись: https://www.youtube.com/watch?v=X3mzNitI-C0 67 | 68 | **lab6_bitwriter.cpp** 69 | 70 | Пример реализации логики побитовой записи: накапливаем вывод во внутреннем буфере. 71 | Текстовое описание устройства функций Encode/Decode: https://pastebin.com/raw/CefJ2ykB 72 | 73 | Семинар 7 74 | --------- 75 | Запись: https://www.youtube.com/watch?v=3RKYZmzhk-g 76 | 77 | **lab7_listgraph.cpp** 78 | 79 | Реализация графа на списках смежности. Вам нужно реализовать MatrixGraph (матрица смежности), SetGraph (вектор хеш-таблиц или сбалансированных бинарных деревьев поиска), ArcGraph (вектор ребер). Поскольку для задачи нет тестов в контесте, нужно продемонстрировать в коде работу конструктора копирования, проинициализировав графы разных классов друг от друга. Далее, показать, что вершины и ребра успешно скопированы: число вершин осталось прежним, произвести BFS и DFS. 80 | ``` 81 | // ListGraph listGraph(N); 82 | // listGraph.AddEdge(...); 83 | // ... 84 | // ArcGraph arcGraph(listGraph); 85 | // MatrixGraph matrixGraph(arcGraph); 86 | // SetGraph setGraph(matrixGraph); 87 | ``` 88 | 89 | Семинар 8 90 | --------- 91 | Запись: https://www.youtube.com/watch?v=xw27AMOj5m8 92 | 93 | **lab8_8game.cpp** 94 | 95 | Решение "восьминашек" через обход в ширину. Вам же нужно перенести этот принцип на решение пятнашек, но "в лоб" это сделать не получится -- размерность пятнашек слишком большая, поэтому вместо простого BFS перейдем на A\*. Кроме того, ограничим в нем размер очереди вершин (каким образом -- решать вам), чтобы влезать в ограничения по памяти. Скорее всего из-за такого хака алгоритм упустит кратчайший путь, но нам это и не важно в данной задаче. То есть цель -- найти хоть какое решение, не обязательно оптимальное. 96 | 97 | Семинар 9 98 | --------- 99 | Запись: https://www.youtube.com/watch?v=HegTZU1lLC0 100 | 101 | **lab9_dsu.cpp** 102 | 103 | Реализация структуры данных "система непересекающихся множеств" (Disjoint Set Union) для алгоритма Крускала. 104 | -------------------------------------------------------------------------------- /lab1_queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Node 6 | { 7 | Node(int data) 8 | : data(data), next(nullptr) 9 | { 10 | } 11 | int data; 12 | Node *next; 13 | }; 14 | 15 | class Queue 16 | { 17 | public: 18 | Queue() 19 | : head(nullptr), tail(nullptr) 20 | { 21 | } 22 | 23 | // правило "трех": 24 | // раз написали свой деструктор, то надо либо написать 25 | // нормальные конструктор копирования и оператор присваивания, 26 | // либо вообще от них отказаться 27 | Queue(const Queue &other) = delete; 28 | Queue& operator=(const Queue &other) = delete; 29 | 30 | ~Queue() 31 | { 32 | while (!IsEmpty()) 33 | { 34 | Dequeue(); 35 | } 36 | } 37 | 38 | void Enqueue(int data) 39 | { 40 | Node *node = new Node(data); 41 | if (IsEmpty()) 42 | { 43 | head = tail = node; 44 | return; 45 | } 46 | tail->next = node; 47 | tail = node; 48 | } 49 | 50 | int Dequeue() 51 | { 52 | if (IsEmpty()) 53 | return -1; 54 | 55 | int tmpData = head->data; 56 | Node *tmpNode = head; 57 | 58 | head = head->next; 59 | if (head == nullptr) 60 | { 61 | tail = nullptr; 62 | } 63 | 64 | delete tmpNode; 65 | return tmpData; 66 | } 67 | 68 | bool IsEmpty() 69 | { 70 | return head == nullptr && tail == nullptr; 71 | } 72 | 73 | private: 74 | Node *head; 75 | Node *tail; 76 | }; 77 | 78 | void testLogic() 79 | { 80 | Queue queue; 81 | int sampleCount = 100; 82 | 83 | assert(queue.IsEmpty()); 84 | 85 | for (int i = 0; i < sampleCount; i++) 86 | { 87 | queue.Enqueue(i); 88 | } 89 | 90 | assert(!queue.IsEmpty()); 91 | 92 | for (int i = 0; i < sampleCount; i++) 93 | { 94 | assert(i == queue.Dequeue()); 95 | } 96 | 97 | assert(queue.IsEmpty()); 98 | } 99 | 100 | void run(std::istream &input, std::ostream &output) 101 | { 102 | Queue queue; 103 | 104 | int n = 0; 105 | input >> n; 106 | 107 | for (int i = 0; i < n; i++) 108 | { 109 | int op = 0, val = 0; 110 | input >> op >> val; 111 | 112 | switch (op) 113 | { 114 | case 2: 115 | { 116 | int tmpVal = queue.Dequeue(); 117 | if (tmpVal != val) 118 | { 119 | output << "NO" << std::endl; 120 | return; 121 | } 122 | break; 123 | } 124 | case 3: 125 | { 126 | queue.Enqueue(val); 127 | break; 128 | } 129 | } 130 | } 131 | output << "YES" << std::endl; 132 | } 133 | 134 | void testQueue() 135 | { 136 | { 137 | std::stringstream input, output; 138 | input << "3" << std::endl; 139 | input << "3 111" << std::endl; 140 | input << "2 222" << std::endl; 141 | input << "3 333" << std::endl; 142 | run(input, output); 143 | assert(output.str() == "NO\n"); 144 | } 145 | { 146 | std::stringstream input, output; 147 | input << "3" << std::endl; 148 | input << "3 111" << std::endl; 149 | input << "2 111" << std::endl; 150 | input << "3 333" << std::endl; 151 | run(input, output); 152 | assert(output.str() == "YES\n"); 153 | } 154 | } 155 | 156 | int main(int argc, const char * argv[]) { 157 | //testLogic(); 158 | run(std::cin, std::cout); 159 | //testQueue(); 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /lab2_comparators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Point 5 | { 6 | Point() 7 | : x(0), y(0) 8 | { 9 | } 10 | Point(int x, int y) 11 | : x(x), y(y) 12 | { 13 | } 14 | int x, y; 15 | }; 16 | 17 | // недостаток оператора <: он может быть только один 18 | bool operator<(const Point &l, const Point &r) 19 | { 20 | return l.x > r.x; 21 | } 22 | 23 | bool pointXLessComparator(const Point &l, const Point &r) 24 | { 25 | return l.x < r.x; 26 | } 27 | 28 | bool pointYLessComparator(const Point &l, const Point &r) 29 | { 30 | return l.y < r.y; 31 | } 32 | 33 | std::istream& operator>>(std::istream &in, Point &point) 34 | { 35 | in >> point.x >> point.y; 36 | return in; 37 | } 38 | 39 | std::ostream& operator<<(std::ostream &out, const Point &point) 40 | { 41 | out << "(" << point.x << ", " << point.y << ")"; 42 | return out; 43 | } 44 | 45 | template 46 | bool defaultLess(const T &l, const T &r) 47 | { 48 | return l < r; 49 | } 50 | 51 | // Компаратор, сравнивающий две точки по их близости к точке p0 52 | class PointComparator 53 | { 54 | public: 55 | PointComparator() 56 | { 57 | } 58 | PointComparator(const Point &p0) 59 | : p0(p0) 60 | { 61 | } 62 | 63 | // оператор () позволяет вызывать объекты данного класса как функцию 64 | bool operator()(const Point &l, const Point &r) 65 | { 66 | auto distance1 = std::sqrt((p0.x - l.x)*(p0.x - l.x) + (p0.y - l.y)*(p0.y - l.y)); 67 | auto distance2 = std::sqrt((p0.x - r.x)*(p0.x - r.x) + (p0.y - r.y)*(p0.y - r.y)); 68 | return distance1 < distance2; 69 | } 70 | private: 71 | Point p0; 72 | }; 73 | 74 | /* 75 | // Как пробросить компаратор через шаблон 76 | template 77 | class Heap 78 | { 79 | public: 80 | Heap() 81 | : cmp(...) // инициализируем конструктор (если у него есть конструктор по умолчанию, можно опустить строчку) 82 | { 83 | 84 | } 85 | private: 86 | void siftDown(...) 87 | { 88 | ... 89 | cmp(arr[i], arr[ch1]) 90 | ... 91 | } 92 | Comparator cmp; 93 | }; 94 | */ 95 | 96 | // компаратор в виде указателя на функцию 97 | // если не указать явно, по умолчанию будет использоваться defaultLess 98 | template 99 | void mySort(T *arr, int l, int r, bool (*cmp)(const T &l, const T &r) = defaultLess) 100 | { 101 | for (int i = l; i < r; i++) 102 | { 103 | for (int j = l; j < r; j++) 104 | { 105 | if (cmp(arr[j + 1], arr[j])) 106 | { 107 | std::swap(arr[j + 1], arr[j]); 108 | } 109 | } 110 | } 111 | } 112 | 113 | 114 | // теперь в качестве компаратора можно передавать что угодно, лишь бы это можно было вызвать при помощи () 115 | // если явно не передавать компаратор, то в качестве него будет использован объект типа std::less 116 | template > 117 | void mySort2(T *arr, int l, int r, Comparator cmp = Comparator()) 118 | { 119 | for (int i = l; i < r; i++) 120 | { 121 | for (int j = l; j < r; j++) 122 | { 123 | if (cmp(arr[j + 1], arr[j])) 124 | { 125 | std::swap(arr[j + 1], arr[j]); 126 | } 127 | } 128 | } 129 | } 130 | 131 | int main(int argc, const char * argv[]) { 132 | int n = 0; 133 | std::cin >> n; 134 | Point *arr = new Point[n]; 135 | for (int i = 0; i < n; i++) 136 | { 137 | std::cin >> arr[i]; 138 | } 139 | 140 | //Point p0; 141 | //std::cin >> p0; 142 | 143 | // В шаблонных функциях не обязательно указывать шаблонные аргументы. Компилятор сам вычислит фактические типы T и Comparator на основе переданных аргументов. Но можно и явно указать. 144 | // Эти два вызова эквивалентны: 145 | // mySort2(arr, 0, n - 1); 146 | // mySort2(arr, 0, n - 1, PointComparator()); 147 | 148 | // При создании объектов шаблонного класса обязательно нужно указывать все шаблонные типы, у которых не определен тип по умолчанию. 149 | 150 | mySort2(arr, 0, n - 1); 151 | 152 | for (int i = 0; i < n; i++) 153 | { 154 | std::cout << arr[i] << " "; 155 | } 156 | std::cout << std::endl; 157 | 158 | delete[] arr; 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /lab3_counting_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void counting_sort(int *arr, int n) 5 | { 6 | assert( arr != nullptr ); 7 | assert( n > 0 ); 8 | 9 | int minVal = arr[0]; 10 | int maxVal = arr[0]; 11 | 12 | for (int i = 1; i < n; i++) 13 | { 14 | minVal = std::min(minVal, arr[i]); 15 | maxVal = std::max(maxVal, arr[i]); 16 | } 17 | 18 | int countBufSize = maxVal - minVal + 1; 19 | int *countBuf = new int[countBufSize](); 20 | 21 | for (int i = 0; i < n; i++) 22 | { 23 | countBuf[arr[i] - minVal]++; 24 | } 25 | 26 | int *tmpBuf = new int[n](); 27 | 28 | for (int i = 1; i < countBufSize; i++) 29 | { 30 | countBuf[i] += countBuf[i - 1]; 31 | } 32 | 33 | for (int i = n - 1; i >= 0; i--) 34 | { 35 | int countBufPos = arr[i] - minVal; 36 | countBuf[countBufPos]--; 37 | tmpBuf[countBuf[countBufPos]] = arr[i]; 38 | } 39 | 40 | for (int i = 0; i < n; i++) 41 | { 42 | arr[i] = tmpBuf[i]; 43 | } 44 | 45 | /* 46 | int p = 0; 47 | for (int i = 0; i < countBufSize; i++) 48 | { 49 | for (int j = 0; j < countBuf[i]; j++) 50 | { 51 | arr[p++] = i + minVal; 52 | } 53 | } 54 | */ 55 | 56 | delete[] countBuf; 57 | delete[] tmpBuf; 58 | } 59 | 60 | int main(int argc, const char * argv[]) { 61 | int n = 0; 62 | std::cin >> n; 63 | 64 | int *arr = new int[n]; 65 | for (int i = 0; i < n; i++) 66 | { 67 | std::cin >> arr[i]; 68 | } 69 | 70 | counting_sort(arr, n); 71 | 72 | for (int i = 0; i < n; i++) 73 | { 74 | std::cout << arr[i] << " "; 75 | } 76 | std::cout << std::endl; 77 | 78 | delete[] arr; 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /lab3_partition.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template > 5 | int partition(T *arr, int l, int r, Comparator cmp = Comparator()) 6 | { 7 | T pivot = arr[l]; 8 | int i = l + 1; 9 | int j = r; 10 | 11 | while (1) 12 | { 13 | while (i <= r && cmp(arr[i], pivot)) 14 | i++; 15 | while (cmp(pivot, arr[j])) 16 | j--; 17 | 18 | if (i >= j) 19 | { 20 | std::swap(arr[l], arr[j]); 21 | return j; 22 | } 23 | 24 | std::swap(arr[i], arr[j]); 25 | i++; 26 | j--; 27 | } 28 | } 29 | 30 | template > 31 | T kth_statistic(T *arr, int k, int l, int r, Comparator cmp = Comparator()) 32 | { 33 | int pivot_pos = partition(arr, l, r, cmp); 34 | 35 | if (pivot_pos == k) 36 | { 37 | return arr[pivot_pos]; 38 | } 39 | if (pivot_pos > k) 40 | { 41 | return kth_statistic(arr, k, l, pivot_pos - 1, cmp); 42 | } 43 | return kth_statistic(arr, k, pivot_pos + 1, r, cmp); 44 | } 45 | 46 | int main(int argc, const char * argv[]) { 47 | int n = 0; 48 | std::cin >> n; 49 | 50 | std::string *arr = new std::string[n]; 51 | for (int i = 0; i < n; i++) 52 | { 53 | std::cin >> arr[i]; 54 | } 55 | 56 | for (int i = 0; i < n; i++) 57 | { 58 | std::cout << kth_statistic(arr, i, 0, n - 1) << " "; 59 | } 60 | std::cout << std::endl; 61 | 62 | delete[] arr; 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /lab4_hashtable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const size_t DEFAULT_SIZE = 8; 5 | 6 | template 7 | struct HashtableNode 8 | { 9 | HashtableNode() 10 | : next(nullptr) 11 | { 12 | } 13 | 14 | HashtableNode(const T &data, HashtableNode *next) 15 | : data(data), next(next) 16 | { 17 | } 18 | 19 | T data; 20 | HashtableNode *next; 21 | }; 22 | 23 | class StringHasher 24 | { 25 | public: 26 | StringHasher(size_t prime = 71) 27 | : prime(prime) 28 | { 29 | } 30 | 31 | size_t operator()(const std::string &str) 32 | { 33 | size_t hash = 0; 34 | for (int i = 0; i < str.size(); i++) 35 | { 36 | hash = hash * prime + str[i]; 37 | } 38 | return hash; 39 | } 40 | 41 | private: 42 | size_t prime; 43 | }; 44 | 45 | template 46 | class Hashtable 47 | { 48 | public: 49 | Hashtable(size_t initial_size = DEFAULT_SIZE) 50 | : size(0), table(initial_size, nullptr) 51 | { 52 | } 53 | 54 | ~Hashtable() 55 | { 56 | for (int i = 0; i < table.size(); i++) 57 | { 58 | HashtableNode *node = table[i]; 59 | 60 | while (node != nullptr) 61 | { 62 | HashtableNode *next = node->next; 63 | delete node; 64 | node = next; 65 | } 66 | } 67 | } 68 | 69 | bool Add(const T &key) 70 | { 71 | if (size > table.size() * 2) 72 | { 73 | grow(); 74 | } 75 | 76 | size_t hash = hasher(key) % table.size(); 77 | HashtableNode *node = table[hash]; 78 | 79 | while (node != nullptr) 80 | { 81 | if (node->data == key) 82 | { 83 | return false; 84 | } 85 | node = node->next; 86 | } 87 | 88 | table[hash] = new HashtableNode(key, table[hash]); 89 | size++; 90 | return true; 91 | } 92 | 93 | bool Has(const T &key) 94 | { 95 | size_t hash = hasher(key) % table.size(); 96 | HashtableNode *node = table[hash]; 97 | 98 | while (node != nullptr) 99 | { 100 | if (node->data == key) 101 | { 102 | return true; 103 | } 104 | node = node->next; 105 | } 106 | return false; 107 | } 108 | 109 | bool Delete(const T &key) 110 | { 111 | size_t hash = hasher(key) % table.size(); 112 | 113 | HashtableNode *node = table[hash]; 114 | HashtableNode *prev = nullptr; 115 | 116 | while (node != nullptr) 117 | { 118 | if (node->data == key) 119 | { 120 | break; 121 | } 122 | prev = node; 123 | node = node->next; 124 | } 125 | 126 | if (node == nullptr) 127 | return false; 128 | 129 | if (prev == nullptr) 130 | { 131 | table[hash] = node->next; 132 | } 133 | else 134 | { 135 | prev->next = node->next; 136 | } 137 | 138 | delete node; 139 | size--; 140 | return true; 141 | } 142 | 143 | private: 144 | void grow() 145 | { 146 | std::vector*> newTable(table.size() * 2, nullptr); 147 | 148 | for (int i = 0; i < table.size(); i++) 149 | { 150 | HashtableNode *node = table[i]; 151 | 152 | while (node != nullptr) 153 | { 154 | HashtableNode *next = node->next; 155 | size_t newHash = hasher(node->data) % newTable.size(); 156 | node->next = newTable[newHash]; 157 | newTable[newHash] = node; 158 | node = next; 159 | } 160 | } 161 | 162 | std::swap(table, newTable); 163 | } 164 | 165 | std::vector*> table; 166 | size_t size; 167 | Hasher hasher; 168 | }; 169 | 170 | int main(int argc, const char * argv[]) { 171 | Hashtable table; 172 | 173 | char op; 174 | std::string key; 175 | 176 | while (std::cin >> op >> key) 177 | { 178 | switch (op) 179 | { 180 | case '?': 181 | { 182 | std::cout << (table.Has(key) ? "OK" : "FAIL") << std::endl; 183 | break; 184 | } 185 | case '+': 186 | { 187 | std::cout << (table.Add(key) ? "OK" : "FAIL") << std::endl; 188 | break; 189 | } 190 | case '-': 191 | { 192 | std::cout << (table.Delete(key) ? "OK" : "FAIL") << std::endl; 193 | break; 194 | } 195 | case '!': 196 | { 197 | return 0; 198 | } 199 | 200 | } 201 | } 202 | return 0; 203 | } 204 | -------------------------------------------------------------------------------- /lab5_avltree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // дописать компаратор 4 | template 5 | class AvlTree 6 | { 7 | struct Node 8 | { 9 | Node(const T &data) 10 | : data(data), left(nullptr), right(nullptr), height(1) 11 | { 12 | } 13 | 14 | T data; 15 | Node *left; 16 | Node *right; 17 | size_t height; 18 | }; 19 | 20 | public: 21 | AvlTree() 22 | : root(nullptr) 23 | { 24 | } 25 | 26 | ~AvlTree() 27 | { 28 | destroyTree(root); 29 | } 30 | 31 | void Add(const T &data) 32 | { 33 | root = addInternal(root, data); 34 | } 35 | 36 | bool Has(const T &data) 37 | { 38 | Node *tmp = root; 39 | while (tmp) 40 | { 41 | if (tmp->data == data) 42 | return true; 43 | else if (tmp->data < data) 44 | tmp = tmp->right; 45 | else 46 | tmp = tmp->left; 47 | } 48 | return false; 49 | } 50 | void Delete(const T &data) 51 | { 52 | root = deleteInternal(root, data); 53 | } 54 | private: 55 | 56 | void destroyTree(Node *node) 57 | { 58 | if (node) 59 | { 60 | destroyTree(node->left); 61 | destroyTree(node->right); 62 | delete node; 63 | } 64 | } 65 | 66 | Node* deleteInternal(Node *node, const T &data) 67 | { 68 | if (!node) 69 | return nullptr; 70 | if (node->data < data) 71 | node->right = deleteInternal(node->right, data); 72 | else if (node->data > data) 73 | node->left = deleteInternal(node->left, data); 74 | else 75 | { 76 | Node *left = node->left; 77 | Node *right = node->right; 78 | 79 | delete node; 80 | 81 | if (!right) 82 | return left; 83 | 84 | // поддерево, из которого берем элемент взамен удаляемого, выбираем на основе сравнения глубин. 85 | // (берем из более глубокого) 86 | 87 | // findMin и removeMin объединить в один метод findAndRemoveMin/findAndRemoveMax 88 | Node *min = findMin(right); // возвращает минимальный элемент в дереве 89 | min->right = removeMin(right); // возвращает дерево, из которого удалили минимальный элемент 90 | min->left = left; 91 | 92 | return doBalance(min); 93 | } 94 | 95 | return doBalance(node); 96 | } 97 | 98 | Node* findMin(Node *node) 99 | { 100 | while (node->left) 101 | node = node->left; 102 | return node; 103 | } 104 | 105 | Node* removeMin(Node *node) 106 | { 107 | if (!node->left) 108 | return node->right; 109 | node->left = removeMin(node->left); 110 | return doBalance(node); 111 | } 112 | 113 | Node* addInternal(Node *node, const T &data) 114 | { 115 | if (!node) 116 | return new Node(data); 117 | if (node->data <= data) 118 | node->right = addInternal(node->right, data); 119 | else 120 | node->left = addInternal(node->left, data); 121 | 122 | return doBalance(node); 123 | } 124 | 125 | size_t getHeight(Node *node) 126 | { 127 | return node ? node->height : 0; 128 | } 129 | 130 | void fixHeight(Node *node) 131 | { 132 | node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1; 133 | } 134 | 135 | int getBalance(Node *node) 136 | { 137 | return getHeight(node->right) - getHeight(node->left); 138 | } 139 | 140 | Node* rotateLeft(Node *node) 141 | { 142 | Node *tmp = node->right; 143 | node->right = tmp->left; 144 | tmp->left = node; 145 | fixHeight(node); 146 | fixHeight(tmp); 147 | return tmp; 148 | } 149 | 150 | Node* rotateRight(Node *node) 151 | { 152 | Node *tmp = node->left; 153 | node->left = tmp->right; 154 | tmp->right = node; 155 | fixHeight(node); 156 | fixHeight(tmp); 157 | return tmp; 158 | } 159 | 160 | Node* doBalance(Node *node) 161 | { 162 | fixHeight(node); 163 | switch (getBalance(node)) 164 | { 165 | case 2: 166 | { 167 | if (getBalance(node->right) < 0) 168 | node->right = rotateRight(node->right); 169 | return rotateLeft(node); 170 | } 171 | case -2: 172 | { 173 | if (getBalance(node->left) > 0) 174 | node->left = rotateLeft(node->left); 175 | return rotateRight(node); 176 | } 177 | default: 178 | return node; 179 | } 180 | } 181 | 182 | Node *root; 183 | }; 184 | 185 | int main(int argc, const char * argv[]) { 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /lab5_btree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template 5 | class BTree 6 | { 7 | public: 8 | struct Node 9 | { 10 | Node(bool leaf) 11 | : leaf(leaf) 12 | { 13 | } 14 | 15 | ~Node() 16 | { 17 | for (Node* child: children) 18 | { 19 | delete child; 20 | } 21 | } 22 | 23 | bool leaf; 24 | std::vector keys; 25 | std::vector children; 26 | }; 27 | 28 | BTree(size_t min_degree) 29 | : t(min_degree), root(nullptr) 30 | { 31 | } 32 | 33 | ~BTree() 34 | { 35 | if (root) 36 | delete root; 37 | } 38 | 39 | void Insert(const T &key) 40 | { 41 | if (!root) 42 | root = new Node(true); 43 | 44 | // здесь и дальше: если при спуске по дереву находим 45 | // переполненный узел -- сначала разбиваем его, потом спускаемся 46 | if (isNodeFull(root)) 47 | { 48 | Node *newRoot = new Node(false); 49 | newRoot->children.push_back(root); 50 | root = newRoot; 51 | splitChild(root, 0); 52 | } 53 | 54 | // теперь корень точно не переполнен, можем вызвать insertNonFull 55 | insertNonFull(root, key); 56 | } 57 | 58 | void debugPrint() 59 | { 60 | debugPrintInternal(root, 0); 61 | } 62 | 63 | private: 64 | 65 | void debugPrintInternal(Node *node, int indent) 66 | { 67 | std::cout << std::string(indent, ' '); 68 | std::cout << "keys: ["; 69 | for (auto it = node->keys.begin(); it != node->keys.end(); it++) 70 | { 71 | std::cout << (*it); 72 | if (it + 1 != node->keys.end()) 73 | std::cout << ", "; 74 | } 75 | std::cout << "]" << std::endl; 76 | 77 | for (auto child: node->children) 78 | { 79 | debugPrint(child, indent + 4); 80 | } 81 | } 82 | 83 | bool isNodeFull(Node *node) 84 | { 85 | return node->keys.size() == 2*t - 1; 86 | } 87 | 88 | // разбить переполненного потомка index узла node 89 | void splitChild(Node *node, size_t index); 90 | 91 | // вставить ключ key в гарантированно не переполненную ноду node 92 | void insertNonFull(Node *node, const T &key) 93 | { 94 | int pos = node->keys.size() - 1; 95 | 96 | // гарантированно не перепеполненный лист -- запишем новый ключ в него 97 | if (node->leaf) 98 | { 99 | // расширили вектор ключей для вставки нового 100 | node->keys.resize(node->keys.size() + 1); 101 | while (pos >= 0 && key < node->keys[pos]) 102 | { 103 | // обходим ключи справа налево, сдвигая вправо на 1 104 | node->keys[pos + 1] = node->keys[pos]; 105 | pos--; 106 | } 107 | // вставляем новый ключ на освобожденное в цикле место 108 | node->keys[pos + 1] = key; 109 | } 110 | // не лист, значит есть потомки, пишем в один из них 111 | else 112 | { 113 | // ищем позицию потомка, в которого добавим ключ 114 | while (pos >= 0 && key < node->keys[pos]) 115 | { 116 | pos--; 117 | } 118 | 119 | // если потомок и так полон, надо его сначала разбить 120 | if (isNodeFull(node->children[pos + 1])) 121 | { 122 | splitChild(node, pos + 1); 123 | // после разбиения потомка в текущий узел из него поднялся ключ 124 | // надо сравниться и с ним 125 | if (key > node->keys[pos + 1]) 126 | pos++; 127 | } 128 | insertNonFull(node->children[pos + 1], key); 129 | } 130 | } 131 | 132 | size_t t; 133 | Node *root; 134 | 135 | friend void test1(); 136 | }; 137 | 138 | // случаи вставки с иллюстраций в лекции 139 | void test1() 140 | { 141 | BTree tree(3); 142 | 143 | tree.root = new BTree::Node(false); 144 | tree.root->keys = {'G', 'M', 'P', 'X'}; 145 | 146 | { 147 | auto child = new BTree::Node(true); 148 | child->keys = {'A', 'C', 'D', 'E'}; 149 | tree.root->children.push_back(child); 150 | } 151 | 152 | { 153 | auto child = new BTree::Node(true); 154 | child->keys = {'J', 'K'}; 155 | tree.root->children.push_back(child); 156 | } 157 | { 158 | auto child = new BTree::Node(true); 159 | child->keys = {'N', 'O'}; 160 | tree.root->children.push_back(child); 161 | } 162 | { 163 | auto child = new BTree::Node(true); 164 | child->keys = {'R', 'S', 'T', 'U', 'V'}; 165 | tree.root->children.push_back(child); 166 | } 167 | { 168 | auto child = new BTree::Node(true); 169 | child->keys = {'Y', 'Z'}; 170 | tree.root->children.push_back(child); 171 | } 172 | 173 | std::cout << "Initial tree:" << std::endl; 174 | tree.debugPrint(); 175 | std::cout << std::endl; 176 | 177 | std::string insertKeys = "BQLF"; 178 | // посимвольно добавляем в дерево ключи 179 | for (auto c: insertKeys) 180 | { 181 | tree.Insert(c); 182 | std::cout << "After inserting " << c << ":" << std::endl; 183 | tree.debugPrint(); 184 | std::cout << std::endl; 185 | } 186 | } 187 | 188 | int main(int argc, const char * argv[]) { 189 | test1(); 190 | return 0; 191 | } 192 | -------------------------------------------------------------------------------- /lab6_bitwriter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class BitWriter 6 | { 7 | public: 8 | BitWriter() 9 | : bitCount(0) 10 | { 11 | } 12 | 13 | void WriteBit(unsigned char bit) 14 | { 15 | if (bitCount % 8 == 0) 16 | { 17 | buffer.push_back(0); 18 | } 19 | if (bit) 20 | { 21 | buffer[bitCount/8] |= 1 << (7 - bitCount % 8); 22 | } 23 | bitCount++; 24 | } 25 | 26 | void WriteByte(unsigned char byte) 27 | { 28 | if (bitCount % 8 == 0) 29 | { 30 | buffer.push_back(byte); 31 | } 32 | else 33 | { 34 | int offset = bitCount % 8; 35 | buffer[bitCount/8] |= byte >> offset; 36 | buffer.push_back(byte << (8 - offset)); 37 | } 38 | bitCount += 8; 39 | } 40 | 41 | const std::vector &GetBuffer() const 42 | { 43 | return buffer; 44 | } 45 | 46 | size_t GetBitCount() const 47 | { 48 | return bitCount; 49 | } 50 | 51 | private: 52 | std::vector buffer; 53 | size_t bitCount; 54 | }; 55 | 56 | void visualizeBuffer(const std::vector &buffer) 57 | { 58 | for (auto &b: buffer) 59 | { 60 | std::cout << std::bitset<8>(b) << "|"; 61 | } 62 | std::cout << std::endl; 63 | } 64 | 65 | int main(int argc, const char * argv[]) { 66 | BitWriter bw; 67 | bw.WriteBit(1); 68 | visualizeBuffer(bw.GetBuffer()); 69 | 70 | bw.WriteBit(0); 71 | bw.WriteBit(0); 72 | bw.WriteBit(1); 73 | visualizeBuffer(bw.GetBuffer()); 74 | 75 | bw.WriteByte(255); 76 | bw.WriteBit(0); 77 | bw.WriteBit(1); 78 | visualizeBuffer(bw.GetBuffer()); 79 | std::cout << (bw.GetBitCount() % 8) << std::endl; 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /lab7_listgraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct IGraph { 9 | virtual ~IGraph() {} 10 | 11 | virtual void AddEdge(int from, int to) = 0; 12 | 13 | virtual int VerticesCount() const = 0; 14 | 15 | virtual std::vector GetNextVertices(int vertex) const = 0; 16 | virtual std::vector GetPrevVertices(int vertex) const = 0; 17 | }; 18 | 19 | struct ListGraph: public IGraph 20 | { 21 | public: 22 | ListGraph(int size) 23 | : adjacencyLists(size) 24 | { 25 | } 26 | 27 | ListGraph(const IGraph &graph) 28 | { 29 | for (int i = 0; i < graph.VerticesCount(); ++i) 30 | { 31 | adjacencyLists[i] = graph.GetNextVertices(i); 32 | } 33 | } 34 | 35 | ~ListGraph() 36 | { 37 | } 38 | 39 | void AddEdge(int from, int to) override 40 | { 41 | assert(0 <= from && from < adjacencyLists.size()); 42 | assert(0 <= to && to < adjacencyLists.size()); 43 | adjacencyLists[from].push_back(to); 44 | } 45 | 46 | int VerticesCount() const override 47 | { 48 | return (int)adjacencyLists.size(); 49 | } 50 | 51 | std::vector GetNextVertices(int vertex) const override 52 | { 53 | assert(0 <= vertex && vertex < adjacencyLists.size()); 54 | return adjacencyLists[vertex]; 55 | } 56 | 57 | std::vector GetPrevVertices(int vertex) const override 58 | { 59 | assert(0 <= vertex && vertex < adjacencyLists.size()); 60 | std::vector prevVertices; 61 | 62 | for (int from = 0; from < adjacencyLists.size(); ++from) 63 | { 64 | for (int to: adjacencyLists[from]) 65 | { 66 | if (to == vertex) 67 | prevVertices.push_back(from); 68 | } 69 | } 70 | return prevVertices; 71 | } 72 | 73 | private: 74 | std::vector> adjacencyLists; 75 | }; 76 | 77 | 78 | void BFS(const IGraph &graph, int vertex, std::vector &visited, const std::function &func) 79 | { 80 | std::queue qu; 81 | qu.push(vertex); 82 | visited[vertex] = true; 83 | 84 | while (!qu.empty()) 85 | { 86 | int currentVertex = qu.front(); 87 | qu.pop(); 88 | 89 | func(currentVertex); 90 | 91 | for (int nextVertex: graph.GetNextVertices(currentVertex)) 92 | { 93 | if (!visited[nextVertex]) 94 | { 95 | visited[nextVertex] = true; 96 | qu.push(nextVertex); 97 | } 98 | } 99 | } 100 | } 101 | 102 | void mainBFS(const IGraph &graph, const std::function &func) 103 | { 104 | std::vector visited(graph.VerticesCount(), false); 105 | 106 | for (int i = 0; i < graph.VerticesCount(); ++i) 107 | { 108 | if (!visited[i]) 109 | { 110 | BFS(graph, i, visited, func); 111 | } 112 | } 113 | } 114 | 115 | void DFS(const IGraph &graph, int vertex, std::vector &visited, const std::function &func) 116 | { 117 | visited[vertex] = true; 118 | func(vertex); 119 | 120 | for (int nextVertex: graph.GetNextVertices(vertex)) 121 | { 122 | if (!visited[nextVertex]) 123 | { 124 | DFS(graph, nextVertex, visited, func); 125 | } 126 | } 127 | } 128 | 129 | void mainDFS(const IGraph &graph, const std::function &func) 130 | { 131 | std::vector visited(graph.VerticesCount(), false); 132 | 133 | for (int i = 0; i < graph.VerticesCount(); ++i) 134 | { 135 | if (!visited[i]) 136 | { 137 | DFS(graph, i, visited, func); 138 | } 139 | } 140 | } 141 | 142 | void topologicalSortInternal(const IGraph &graph, int vertex, std::vector &visited, std::deque &sorted) 143 | { 144 | visited[vertex] = true; 145 | 146 | for (int nextVertex: graph.GetNextVertices(vertex)) 147 | { 148 | if (!visited[nextVertex]) 149 | { 150 | topologicalSortInternal(graph, nextVertex, visited, sorted); 151 | } 152 | } 153 | 154 | sorted.push_front(vertex); 155 | } 156 | 157 | std::deque topologicalSort(const IGraph &graph) 158 | { 159 | std::deque sorted; 160 | std::vector visited(graph.VerticesCount(), false); 161 | 162 | for (int i = 0; i < graph.VerticesCount(); ++i) 163 | { 164 | if (!visited[i]) 165 | { 166 | topologicalSortInternal(graph, i, visited, sorted); 167 | } 168 | } 169 | 170 | return sorted; 171 | } 172 | 173 | int main(int argc, const char * argv[]) { 174 | ListGraph listGraph(7); 175 | listGraph.AddEdge(0, 1); 176 | listGraph.AddEdge(0, 5); 177 | listGraph.AddEdge(1, 2); 178 | listGraph.AddEdge(1, 3); 179 | listGraph.AddEdge(1, 5); 180 | listGraph.AddEdge(1, 6); 181 | listGraph.AddEdge(3, 2); 182 | listGraph.AddEdge(3, 4); 183 | listGraph.AddEdge(3, 6); 184 | listGraph.AddEdge(5, 4); 185 | listGraph.AddEdge(5, 6); 186 | listGraph.AddEdge(6, 4); 187 | 188 | mainBFS(listGraph, [](int vertex){ std::cout << vertex << " "; }); 189 | std::cout << std::endl; 190 | 191 | mainDFS(listGraph, [](int vertex){ std::cout << vertex << " "; }); 192 | std::cout << std::endl; 193 | 194 | for (int vertex: topologicalSort(listGraph)) 195 | { 196 | std::cout << vertex << " "; 197 | } 198 | std::cout << std::endl; 199 | 200 | // Нужно продемонстрировать работу конструктора копирования, проинициализировав 201 | // графы разных классов друг от друга. Далее, показать, что вершины и ребра 202 | // успешно скопированы: число вершин осталось прежним, произвести BFS и DFS. 203 | // MatrixGraph matrixGraph(listGraph); 204 | // ArcGraph arcGraph(matrixGraph); 205 | // SetGraph setGraph(arcGraph); 206 | return 0; 207 | } 208 | -------------------------------------------------------------------------------- /lab8_8game.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const char FieldSize = 9; 9 | const std::array finishField = {1, 2, 3, 4, 5, 6, 7, 8, 0}; 10 | 11 | class GameState 12 | { 13 | public: 14 | GameState(const std::array &field) 15 | : field(field) 16 | { 17 | emptyPos = -1; 18 | for (int i = 0; i < FieldSize; i++) 19 | { 20 | if (field[i] == 0) 21 | { 22 | emptyPos = i; 23 | } 24 | } 25 | assert(emptyPos != -1); 26 | } 27 | 28 | bool IsComplete() const 29 | { 30 | return field == finishField; 31 | } 32 | 33 | bool IsSolvable() const 34 | { 35 | return getInvCount() % 2 == 0; 36 | } 37 | 38 | bool CanMoveLeft() const 39 | { 40 | return emptyPos % 3 != 2; 41 | } 42 | 43 | bool CanMoveRight() const 44 | { 45 | return emptyPos % 3 != 0; 46 | } 47 | 48 | bool CanMoveUp() const 49 | { 50 | return emptyPos < 6; 51 | } 52 | 53 | bool CanMoveDown() const 54 | { 55 | return emptyPos > 2; 56 | } 57 | 58 | GameState MoveLeft() const 59 | { 60 | assert(CanMoveLeft()); 61 | 62 | GameState newState(*this); 63 | std::swap(newState.field[emptyPos], newState.field[emptyPos + 1]); 64 | newState.emptyPos++; 65 | return newState; 66 | } 67 | 68 | GameState MoveRight() const 69 | { 70 | assert(CanMoveRight()); 71 | 72 | GameState newState(*this); 73 | std::swap(newState.field[emptyPos], newState.field[emptyPos - 1]); 74 | newState.emptyPos--; 75 | return newState; 76 | } 77 | 78 | GameState MoveUp() const 79 | { 80 | assert(CanMoveUp()); 81 | 82 | GameState newState(*this); 83 | std::swap(newState.field[emptyPos], newState.field[emptyPos + 3]); 84 | newState.emptyPos += 3; 85 | return newState; 86 | } 87 | 88 | GameState MoveDown() const 89 | { 90 | assert(CanMoveDown()); 91 | 92 | GameState newState(*this); 93 | std::swap(newState.field[emptyPos], newState.field[emptyPos - 3]); 94 | newState.emptyPos -= 3; 95 | return newState; 96 | } 97 | 98 | bool operator==(const GameState &other) const 99 | { 100 | return field == other.field; 101 | } 102 | 103 | private: 104 | size_t getInvCount() const 105 | { 106 | size_t inv_count = 0; 107 | for (int i = 0; i < FieldSize - 1; i++) 108 | { 109 | for (int j = i + 1; j < FieldSize; j++) 110 | { 111 | if (field[i] > field[j] && field[i] && field[j]) 112 | inv_count++; 113 | } 114 | } 115 | return inv_count; 116 | } 117 | 118 | std::array field; 119 | char emptyPos; 120 | 121 | friend struct GameStateHasher; 122 | friend std::ostream& operator<<(std::ostream &out, const GameState &state); 123 | }; 124 | 125 | struct GameStateHasher 126 | { 127 | public: 128 | size_t operator()(const GameState &state) const 129 | { 130 | size_t hash = 0; 131 | std::memcpy(&hash, state.field.data(), sizeof(hash)); 132 | return hash; 133 | } 134 | }; 135 | 136 | std::ostream& operator<<(std::ostream &out, const GameState &state) 137 | { 138 | for (int i = 0; i < 3; i++) 139 | { 140 | for (int j = 0; j < 3; j++) 141 | { 142 | out << static_cast(state.field[i * 3 + j]) << ' '; 143 | } 144 | out << std::endl; 145 | } 146 | return out; 147 | } 148 | 149 | std::string GetSolution(const std::array &field) 150 | { 151 | GameState startState(field); 152 | 153 | if (!startState.IsSolvable()) 154 | return "-1"; 155 | 156 | std::unordered_map visited; 157 | visited[startState] = 'S'; 158 | 159 | std::queue queue; 160 | queue.push(startState); 161 | 162 | while (true) 163 | { 164 | GameState state = queue.front(); 165 | queue.pop(); 166 | 167 | if (state.IsComplete()) 168 | break; 169 | 170 | if (state.CanMoveLeft()) 171 | { 172 | GameState newState = state.MoveLeft(); 173 | if (visited.find(newState) == visited.end()) 174 | { 175 | visited[newState] = 'L'; 176 | queue.push(newState); 177 | } 178 | } 179 | if (state.CanMoveRight()) 180 | { 181 | GameState newState = state.MoveRight(); 182 | if (visited.find(newState) == visited.end()) 183 | { 184 | visited[newState] = 'R'; 185 | queue.push(newState); 186 | } 187 | } 188 | if (state.CanMoveUp()) 189 | { 190 | GameState newState = state.MoveUp(); 191 | if (visited.find(newState) == visited.end()) 192 | { 193 | visited[newState] = 'U'; 194 | queue.push(newState); 195 | } 196 | } 197 | if (state.CanMoveDown()) 198 | { 199 | GameState newState = state.MoveDown(); 200 | if (visited.find(newState) == visited.end()) 201 | { 202 | visited[newState] = 'D'; 203 | queue.push(newState); 204 | } 205 | } 206 | } 207 | 208 | std::string path; 209 | GameState state(finishField); 210 | 211 | std::cout << state << std::endl; 212 | while (visited[state] != 'S') 213 | { 214 | char move = visited[state]; 215 | switch (move) 216 | { 217 | case 'L': 218 | { 219 | state = state.MoveRight(); 220 | path += 'L'; 221 | break; 222 | } 223 | case 'R': 224 | { 225 | state = state.MoveLeft(); 226 | path += 'R'; 227 | break; 228 | } 229 | case 'D': 230 | { 231 | state = state.MoveUp(); 232 | path += 'D'; 233 | break; 234 | } 235 | case 'U': 236 | { 237 | state = state.MoveDown(); 238 | path += 'U'; 239 | break; 240 | } 241 | } 242 | 243 | std::cout << state << std::endl; 244 | } 245 | 246 | std::reverse(path.begin(), path.end()); 247 | return path; 248 | } 249 | 250 | int main(int argc, const char * argv[]) { 251 | std::array field = {2, 3, 1, 4, 5, 6, 7, 8, 0}; 252 | std::cout << GetSolution(field) << std::endl; 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /lab9_dsu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | class DSU 7 | { 8 | public: 9 | DSU(size_t size) 10 | : parent(size), rank(size, 1) 11 | { 12 | for (int i = 0; i < size; i++) 13 | { 14 | parent[i] = i; 15 | } 16 | } 17 | 18 | size_t find_set(size_t u) 19 | { 20 | std::stack stack; 21 | stack.push(u); 22 | 23 | while (parent[u] != u) 24 | { 25 | stack.push(parent[u]); 26 | u = parent[u]; 27 | } 28 | 29 | size_t &root = u; 30 | 31 | while (!stack.empty()) 32 | { 33 | parent[stack.top()] = root; 34 | stack.pop(); 35 | } 36 | 37 | return root; 38 | } 39 | 40 | void union_set(size_t u, size_t v) 41 | { 42 | u = find_set(u); 43 | v = find_set(v); 44 | 45 | if (u != v) 46 | { 47 | if (rank[u] < rank[v]) 48 | std::swap(u, v); 49 | 50 | parent[v] = u; 51 | rank[u] += rank[v]; 52 | } 53 | } 54 | 55 | private: 56 | std::vector parent; 57 | std::vector rank; 58 | 59 | friend std::ostream& operator<<(std::ostream &out, DSU &dsu); 60 | }; 61 | 62 | std::ostream& operator<<(std::ostream &out, DSU &dsu) 63 | { 64 | std::map> sets; 65 | 66 | for (auto i = 0; i < dsu.parent.size(); i++) 67 | { 68 | sets[dsu.find_set(i)].push_back(i); 69 | } 70 | 71 | for (auto &kv: sets) 72 | { 73 | out << kv.first << " [rank = " << dsu.rank[kv.first] << "]: "; 74 | for (auto i = 0; i < kv.second.size(); i++) 75 | { 76 | out << kv.second[i]; 77 | if (i != kv.second.size() - 1) 78 | out << ", "; 79 | } 80 | out << std::endl; 81 | } 82 | 83 | return out; 84 | } 85 | 86 | int main(int argc, const char * argv[]) { 87 | DSU dsu(10); 88 | std::cout << dsu << std::endl; 89 | 90 | dsu.union_set(2, 7); 91 | std::cout << dsu << std::endl; 92 | 93 | dsu.union_set(3, 9); 94 | std::cout << dsu << std::endl; 95 | 96 | dsu.union_set(9, 7); 97 | std::cout << dsu << std::endl; 98 | 99 | dsu.union_set(6, 8); 100 | std::cout << dsu << std::endl; 101 | 102 | dsu.union_set(0, 1); 103 | std::cout << dsu << std::endl; 104 | return 0; 105 | } 106 | --------------------------------------------------------------------------------