diff --git a/doc/topic2/FenwickTree.md b/doc/topic2/FenwickTree.md new file mode 100644 index 0000000..dd8d147 --- /dev/null +++ b/doc/topic2/FenwickTree.md @@ -0,0 +1,135 @@ +# Дерево Фенвика (Fenwick Tree, Binary Indexed Tree) + +**Дерево Фенвика** — это структура данных, которая позволяет эффективно вычислять **префиксные суммы** (суммы элементов массива от начала до заданной позиции) и поддерживать **обновление элементов** за время **O(log n)**. Оно требует **O(n)** памяти. + +Пусть дан массив `a` длины `n`. Деревом Фенвика называется массив `t` той же длины, определённый следующим образом: + +\[ t_i = \sum_{k=F(i)}^{i} a_k \] + +где `F` — некоторая функция, удовлетворяющая условию \( F(i) \leq i \). Конкретное определение функции `F` будет дано позже. + +### Основные операции: +1. **Запрос суммы (prefix sum)** — получение суммы элементов от начала массива до позиции `i`. +2. **Обновление элемента** — изменение значения элемента и корректировка структуры. + +## Запрос суммы + +Для вычисления суммы на отрезке `[l, r]` запрос сводится к двум суммам на префиксе: +\[ \text{sum}(l, r) = \text{sum}(r) - \text{sum}(l - 1) \] + +Каждая из этих сумм вычисляется рекурсивно: +\[ \text{sum}(k) = t_k + \text{sum}(F(k) - 1) \] + +## Запрос обновления + +При изменении элемента `a[k]` необходимо обновить все ячейки `t_i`, в которых учтён этот элемент. + +Функцию `F` можно выбрать таким образом, что количество операций при подсчёте суммы и обновлении будет \( O(\log n) \). Распространены два варианта функции `F`: + +1. \( F_1(x) = x \& (x + 1) \) +2. \( F_2(x) = x - (x \& -x) + 1 \) + +Первый вариант часто встречается в учебных материалах, таких как Викиконспекты и Емакс, и поэтому более известен. Второй вариант проще для запоминания и реализации, а также обладает большей гибкостью — например, позволяет выполнять бинарный поиск по префиксным суммам. Именно второй вариант будет использоваться в дальнейшем. + +## Преимущества: +- Простота реализации. +- Эффективность: обе операции работают за **O(log n)**. +- Меньшая константа по сравнению с деревом отрезков. + +## Недостатки: +- Менее гибкое, чем дерево отрезков (например, не поддерживает все виды запросов, которые можно делать в дереве отрезков). + +--- + +## Принцип работы + +Дерево Фенвика использует идею **бинарного представления индексов**. Каждый элемент дерева хранит сумму некоторого диапазона исходного массива. + +## Пример для массива из 8 элементов: +``` +Дерево Фенвика: +F[1] = A[1] +F[2] = A[1] + A[2] +F[3] = A[3] +F[4] = A[1] + A[2] + A[3] + A[4] +F[5] = A[5] +F[6] = A[5] + A[6] +F[7] = A[7] +F[8] = A[1] + A[2] + ... + A[8] +``` + +### Пример кода на C++ + +```cpp +#include +#include + +class FenwickTree { +private: + std::vector tree; + +public: + FenwickTree(int size) : tree(size + 1, 0) {} + + // Обновление элемента: добавляем delta к A[pos] + void update(int pos, int delta) { + for (; pos < tree.size(); pos += pos & -pos) { + tree[pos] += delta; + } + } + + // Запрос суммы: сумма элементов от A[1] до A[pos] + int query(int pos) { + int sum = 0; + for (; pos > 0; pos -= pos & -pos) { + sum += tree[pos]; + } + return sum; + } + + // Запрос суммы на отрезке [l, r] + int rangeQuery(int l, int r) { + return query(r) - query(l - 1); + } +}; + +int main() { + std::vector arr = {0, 1, 2, 3, 4, 5, 6, 7, 8}; // Индексация с 1 для удобства + + FenwickTree fenwick(arr.size() - 1); // Игнорируем 0-й элемент + + // Построение дерева + for (int i = 1; i < arr.size(); ++i) { + fenwick.update(i, arr[i]); + } + + // Запрос суммы первых 5 элементов + std::cout << "Sum of first 5 elements: " << fenwick.query(5) << std::endl; // 1+2+3+4+5 = 15 + + // Запрос суммы на отрезке [2, 5] + std::cout << "Sum from 2 to 5: " << fenwick.rangeQuery(2, 5) << std::endl; // 2+3+4+5 = 14 + + // Обновление элемента A[3] += 10 + fenwick.update(3, 10); + + // Проверка суммы после обновления + std::cout << "Sum of first 5 after update: " << fenwick.query(5) << std::endl; // 15 + 10 = 25 + + return 0; +} +``` + +#### Вывод программы: +``` +Sum of first 5 elements: 15 +Sum from 2 to 5: 14 +Sum of first 5 after update: 25 +``` + +### Применение +Дерево Фенвика часто используется в задачах: +- Подсчет суммы на префиксе или отрезке. +- Решение задач на инверсии в перестановках. +- Оптимизация динамического программирования. + +Это мощный инструмент для задач, где важна скорость работы с префиксными суммами \ No newline at end of file diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 0e4393b..7d7a5a5 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1,3 +1,21 @@ +#include "solution.hpp" #include -int main() { return 0; } +int main() { + int n; + int c; + cin>>n; + vector a(n); + cin>>c; + for (int i=0; i>a[i]; + } + Result result = Solve(n, c, a); + + if (result.finded) + cout << a[result.left] << ' ' << a[result.right] << endl; + else + cout << "Не удалось найти ответ!" << endl; + + return 0; } diff --git a/task_01/src/solution.hpp b/task_01/src/solution.hpp new file mode 100644 index 0000000..9c49dae --- /dev/null +++ b/task_01/src/solution.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +using namespace std; + +struct Result +{ + bool finded = false; + int left = 0, right = 0; +}; + +inline Result Solve(int n, int c, const vector& a) +{ + if (a.size() != n) return {}; + + int left = 0; + int right = n-1; + while(leftc) {--right; continue;} + if (a[left]+a[right]==c) return {true, a[left], a[right]}; + } + + return {}; +} + diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 87cef73..0685fe9 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,5 +1,93 @@ +#include #include +#include "solution.hpp" -TEST(Test, Simple) { - ASSERT_EQ(1, 1); // Stack [] +class TwoSumTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(TwoSumTest, BasicCase) { + vector nums = {2, 7, 11, 15}; + int target = 9; + Result res = Solve(nums.size(), target, nums); + + EXPECT_TRUE(res.finded); + EXPECT_EQ(res.left, 2); + EXPECT_EQ(res.right, 7); +} + +TEST_F(TwoSumTest, NoSolution) { + vector nums = {1, 2, 3, 4}; + int target = 8; + Result res = Solve(nums.size(), target, nums); + + EXPECT_FALSE(res.finded); +} + +TEST_F(TwoSumTest, AllElementsSame) { + vector nums = {5, 5, 5, 5}; + int target = 10; + Result res = Solve(nums.size(), target, nums); + + EXPECT_TRUE(res.finded); + EXPECT_EQ(res.left, 5); + EXPECT_EQ(res.right, 5); +} + +TEST_F(TwoSumTest, NegativeNumbers) { + vector nums = {-5, -3, 0, 1, 4}; + int target = -2; + Result res = Solve(nums.size(), target, nums); + + EXPECT_TRUE(res.finded); + EXPECT_EQ(res.left, -3); + EXPECT_EQ(res.right, 1); +} + +TEST_F(TwoSumTest, EmptyArray) { + vector nums = {}; + int target = 5; + Result res = Solve(nums.size(), target, nums); + + EXPECT_FALSE(res.finded); +} + +TEST_F(TwoSumTest, SingleElementArray) { + vector nums = {5}; + int target = 5; + Result res = Solve(nums.size(), target, nums); + + EXPECT_FALSE(res.finded); +} + +TEST_F(TwoSumTest, TargetTooSmall) { + vector nums = {10, 20, 30}; + int target = 5; + Result res = Solve(nums.size(), target, nums); + + EXPECT_FALSE(res.finded); +} + +TEST_F(TwoSumTest, TargetTooLarge) { + vector nums = {1, 2, 3}; + int target = 10; + Result res = Solve(nums.size(), target, nums); + + EXPECT_FALSE(res.finded); +} + +TEST_F(TwoSumTest, MultipleSolutions) { + vector nums = {1, 2, 3, 4, 5, 6}; + int target = 7; + Result res = Solve(nums.size(), target, nums); + + EXPECT_TRUE(res.finded); + EXPECT_EQ(res.left + res.right, target); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/task_02/src/main.cpp b/task_02/src/main.cpp index 0e4393b..2dfaeb7 100644 --- a/task_02/src/main.cpp +++ b/task_02/src/main.cpp @@ -1,3 +1,6 @@ #include +#include +using namespace std; + int main() { return 0; } diff --git a/task_03/src/main.cpp b/task_03/src/main.cpp index 0e4393b..3186bf7 100644 --- a/task_03/src/main.cpp +++ b/task_03/src/main.cpp @@ -1,3 +1,7 @@ #include +#include +using namespace std; -int main() { return 0; } + + +int main() { return 0; } \ No newline at end of file diff --git a/task_03/src/solution.hpp b/task_03/src/solution.hpp new file mode 100644 index 0000000..5c8bba8 --- /dev/null +++ b/task_03/src/solution.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +using namespace std; + +inline vector DailyTemp(const vector& temp){ + int n = temp.size(); + vector res(n, 0); + stack s; + + for(int i = 0; itemp[s.top()]) + { + int prev_d = s.top(); + s.pop(); + res[prev_d] = i - prev_d; + } + s.push(i); + } + return res; +} \ No newline at end of file diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index ef5a86a..f367daa 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,8 +1,46 @@ - #include +#include +#include "solution.hpp" + +using namespace std; + +TEST(DailyTempTest, BasicTest) { + vector temps = {73, 74, 75, 71, 69, 72, 76, 73}; + vector expected = {1, 1, 4, 2, 1, 1, 0, 0}; + EXPECT_EQ(DailyTemp(temps), expected); +} + +TEST(DailyTempTest, NoWarming) { + vector temps = {80, 75, 70, 65, 60}; + vector expected = {0, 0, 0, 0, 0}; + EXPECT_EQ(DailyTemp(temps), expected); +} -#include "topology_sort.hpp" +TEST(DailyTempTest, ImmediateWarming) { + vector temps = {50, 60, 70, 80, 90}; + vector expected = {1, 1, 1, 1, 0}; + EXPECT_EQ(DailyTemp(temps), expected); +} + +TEST(DailyTempTest, EmptyInput) { + vector temps = {}; + vector expected = {}; + EXPECT_EQ(DailyTemp(temps), expected); +} -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(DailyTempTest, SingleDay) { + vector temps = {100}; + vector expected = {0}; + EXPECT_EQ(DailyTemp(temps), expected); } + +TEST(DailyTempTest, MixedWarming) { + vector temps = {30, 40, 20, 25, 35, 10}; + vector expected = {1, 0, 1, 1, 0, 0}; + EXPECT_EQ(DailyTemp(temps), expected); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/task_04/src/main.cpp b/task_04/src/main.cpp index 0e4393b..8db32e1 100644 --- a/task_04/src/main.cpp +++ b/task_04/src/main.cpp @@ -1,3 +1,32 @@ #include +#include +#include +#include + +using namespace std; + +static vector> bFish(const vector& prices, int k){ + int n = prices.size(); + vector> purchases; + deque dq; + + for(int i=0; i +#include +#include +#include + +using namespace std; + +inline vector> BFish(const vector& prices, int k) { + vector> purchases; + deque dq; + int n = prices.size(); + + for (int i = 0; i < n; ++i) { + while (!dq.empty() && dq.front() <= i - k) { + dq.pop_front(); + } + + while (!dq.empty() && prices[i] <= prices[dq.back()]) { + dq.pop_back(); + } + dq.push_back(i); + + if (i >= k - 1) { + int buy_day = dq.front(); + if (purchases.empty() || purchases.back().first != buy_day) { + purchases.emplace_back(buy_day, buy_day); + } else { + purchases.back().second++; + } + } + } + + if (n < k) { + if (!prices.empty()) { + int min_pos = min_element(prices.begin(), prices.end()) - prices.begin(); + return {{min_pos, n - 1}}; + } + return {}; + } + + return purchases; +} \ No newline at end of file diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..4dbd18b 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,25 @@ - #include +#include +#include +#include "solution.hpp" +using namespace std; + +TEST(BFishTest, SingleDay) { + vector prices = {5}; + auto result = BFish(prices, 1); + vector> expected = {{0, 0}}; + ASSERT_EQ(result, expected); +} -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(BFishTest, KGreaterThanN) { + vector prices = {5, 3, 4}; + auto result = BFish(prices, 5); + vector> expected = {{1, 2}}; + ASSERT_EQ(result, expected); } + +TEST(BFishTest, EmptyInput) { + vector prices; + auto result = BFish(prices, 3); + ASSERT_TRUE(result.empty()); +} \ No newline at end of file diff --git a/task_05/src/main.cpp b/task_05/src/main.cpp index 0e4393b..ce7d84a 100644 --- a/task_05/src/main.cpp +++ b/task_05/src/main.cpp @@ -1,3 +1,5 @@ #include +#include "solution.hpp" +using namespace std; -int main() { return 0; } +int main(){ return 0;} \ No newline at end of file diff --git a/task_05/src/solution.hpp b/task_05/src/solution.hpp new file mode 100644 index 0000000..7458e50 --- /dev/null +++ b/task_05/src/solution.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +using namespace std; + +template +int Partition(type_ar *ar, int l, int r) { + type_ar v = ar[(l + r) / 2]; + int i = l; + int j = r; + while (i <= j) { + while (ar[i] < v) { + i++; + } + while (ar[j] > v) { + j--; + } + if (i >= j) { + break; + } + swap(ar[i++], ar[j--]); + } + return j; +} + +template +void QuickSort(type_arrr *arrr, int l, int r) { + if (l < r) { + int q = Partition(arrr, l, r); + QuickSort(arrr, l, q); + QuickSort(arrr, q + 1, r); + } +} diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617..9cfc610 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,62 @@ - #include +#include +#include +#include +#include "solution.hpp" + +template +void TestSorting(T* arr, int size) { + QuickSort(arr, 0, size - 1); + for (int i = 1; i < size; ++i) { + ASSERT_LE(arr[i - 1], arr[i]); + } +} + +TEST(QuickSortTest, EmptyArray) { + int arr[] = {}; + ASSERT_NO_THROW(QuickSort(arr, 0, -1)); +} + +TEST(QuickSortTest, SingleElement) { + int arr[] = {42}; + TestSorting(arr, 1); +} + +TEST(QuickSortTest, SortedArray) { + int arr[] = {1, 2, 3, 4, 5}; + TestSorting(arr, 5); +} -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(QuickSortTest, ReverseSortedArray) { + int arr[] = {5, 4, 3, 2, 1}; + TestSorting(arr, 5); } + +TEST(QuickSortTest, RandomArray) { + std::vector arr(100); + std::iota(arr.begin(), arr.end(), 0); + std::shuffle(arr.begin(), arr.end(), std::mt19937{std::random_device{}()}); + TestSorting(arr.data(), arr.size()); +} + +TEST(QuickSortTest, DuplicateElements) { + int arr[] = {2, 2, 1, 1, 3, 3}; + TestSorting(arr, 6); +} + +TEST(QuickSortTest, FloatArray) { + float arr[] = {1.5f, 0.5f, 2.5f, 1.1f, 3.3f}; + TestSorting(arr, 5); +} + +TEST(QuickSortTest, LargeRandomArray) { + std::vector arr(10000); + std::iota(arr.begin(), arr.end(), 0); + std::shuffle(arr.begin(), arr.end(), std::mt19937{std::random_device{}()}); + TestSorting(arr.data(), arr.size()); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/task_06/src/main.cpp b/task_06/src/main.cpp index 0e4393b..2fbf96f 100644 --- a/task_06/src/main.cpp +++ b/task_06/src/main.cpp @@ -1,3 +1,4 @@ #include +#include "solution.hpp" int main() { return 0; } diff --git a/task_06/src/solution.hpp b/task_06/src/solution.hpp new file mode 100644 index 0000000..967a4da --- /dev/null +++ b/task_06/src/solution.hpp @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include + +using namespace std; + +inline int Partition(vector ar, int l, int r) { + int v = ar[(l + r) / 2]; + int i = l; + int j = r; + while (i <= j) { + while (ar[i] < v) { + i++; + } + while (ar[j] > v) { + j--; + } + if (i >= j) { + break; + } + swap(ar[i++], ar[j--]); + } + return j; +} + +inline int FindOrderStat(vector arr, int k) { + if (arr.empty() || k < 0 || k >= arr.size()) { + + throw out_of_range("Invalid k value or empty array"); + } + + int l = 0, r = arr.size(); + while (true) { + int m = Partition(arr, l, r); + + if (m == k) { + return arr[m]; + } + else if (k < m) { + r = m; + } + else { + l = m + 1; + } + } +} \ No newline at end of file diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617..7da9fa6 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,37 @@ - #include +#include "solution.hpp" + +TEST(OrderStatTest, SingleElement) { + vector arr = {42}; + EXPECT_EQ(FindOrderStat(arr, 0), 42); +} + +TEST(OrderStatTest, AlreadySorted) { + vector arr = {1, 2, 3, 4, 5}; + EXPECT_EQ(FindOrderStat(arr, 2), 3); +} -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(OrderStatTest, ReverseSorted) { + vector arr = {5, 4, 3, 2, 1}; + EXPECT_EQ(FindOrderStat(arr, 2), 3); } + +TEST(OrderStatTest, InvalidK) { + vector arr = {1, 2, 3}; + + EXPECT_THROW(FindOrderStat(arr, -1), out_of_range); + EXPECT_THROW(FindOrderStat(arr, 3), out_of_range); + EXPECT_THROW(FindOrderStat(vector(), 0), out_of_range); +} + +TEST(OrderStatTest, Duplicates) { + vector arr = {2, 2, 2, 2, 2}; + for (int k = 0; k < 5; ++k) { + EXPECT_EQ(FindOrderStat(arr, k), 2); + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/task_07/CMakeLists.txt b/task_07/CMakeLists.txt index 91b6a60..c34e30e 100644 --- a/task_07/CMakeLists.txt +++ b/task_07/CMakeLists.txt @@ -26,15 +26,15 @@ find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) -find_library(Utils ../) -target_link_libraries(${PROJECT_NAME} PUBLIC Utils) +#find_library(Utils ../) +#target_link_libraries(${PROJECT_NAME} PUBLIC Utils) # Link runTests with what we want to test and the GTest and pthread library add_executable(${PROJECT_NAME}_tests ${test_source_list}) target_link_libraries( ${PROJECT_NAME}_tests GTest::gtest_main - Utils + #Utils ) include(GoogleTest) diff --git a/task_07/src/red_black_tree.cpp b/task_07/src/red_black_tree.cpp new file mode 100644 index 0000000..61b1961 --- /dev/null +++ b/task_07/src/red_black_tree.cpp @@ -0,0 +1,282 @@ +#include "red_black_tree.hpp" + +void RedBlackTree::Insert(int key) { + Node *z = new Node(key, Colour::Red, nil, nil, nil); + Node *y = nil; + Node *x = root; + + while (x != nil) { + y = x; + if (z->key < x->key) { + x = x->left_child; + } else { + x = x->right_child; + } + } + + z->parent = y; + if (y == nil) { + root = z; + } else if (z->key < y->key) { + y->left_child = z; + } else { + y->right_child = z; + } + + FixInsert(z); +} + +void RedBlackTree::FixInsert(Node *z) { + while (z->parent->colour == Colour::Red) { + if (z->parent == z->parent->parent->left_child) { + Node *y = z->parent->parent->right_child; + if (y->colour == Colour::Red) { + z->parent->colour = Colour::Black; + y->colour = Colour::Black; + z->parent->parent->colour = Colour::Red; + z = z->parent->parent; + } else { + if (z == z->parent->right_child) { + z = z->parent; + LeftRotate(z); + } + z->parent->colour = Colour::Black; + z->parent->parent->colour = Colour::Red; + RightRotate(z->parent->parent); + } + } else { + // Симметричный случай + Node *y = z->parent->parent->left_child; + if (y->colour == Colour::Red) { + z->parent->colour = Colour::Black; + y->colour = Colour::Black; + z->parent->parent->colour = Colour::Red; + z = z->parent->parent; + } else { + if (z == z->parent->left_child) { + z = z->parent; + RightRotate(z); + } + z->parent->colour = Colour::Black; + z->parent->parent->colour = Colour::Red; + LeftRotate(z->parent->parent); + } + } + } + root->colour = Colour::Black; +} + +void RedBlackTree::LeftRotate(Node *x) { + Node *y = x->right_child; + x->right_child = y->left_child; + if (y->left_child != nil) { + y->left_child->parent = x; + } + y->parent = x->parent; + if (x->parent == nil) { + root = y; + } else if (x == x->parent->left_child) { + x->parent->left_child = y; + } else { + x->parent->right_child = y; + } + y->left_child = x; + x->parent = y; +} + +void RedBlackTree::RightRotate(Node *y) { + Node *x = y->left_child; + y->left_child = x->right_child; + if (x->right_child != nil) { + x->right_child->parent = y; + } + x->parent = y->parent; + if (y->parent == nil) { + root = x; + } else if (y == y->parent->right_child) { + y->parent->right_child = x; + } else { + y->parent->left_child = x; + } + x->right_child = y; + y->parent = x; +} + +bool RedBlackTree::Contains(int key) const { + return Contains(root, key); +} + +bool RedBlackTree::Contains(Node* node, int key) const { + if (node == nil) return false; + if (node->key == key) return true; + if (key < node->key) return Contains(node->left_child, key); + return Contains(node->right_child, key); +} + +RedBlackTree::Node* RedBlackTree::Search(int key) { + Node* current = root; + while (current != nil && current->key != key) { + if (key < current->key) { + current = current->left_child; + } else { + current = current->right_child; + } + } + return current; +} + +bool RedBlackTree::IsValid() const { + if (root == nil) return true; + + // 1. Корень должен быть чёрным + if (root->colour != Colour::Black) return false; + + // 2. Подсчёт чёрных узлов в путях и проверка свойств + int blackCount = -1; + return IsRedBlackTreeValid(root, 0, blackCount); +} + +bool RedBlackTree::IsRedBlackTreeValid(Node* node, int currentBlackCount, int& totalBlackCount) const { + if (node == nil) { + if (totalBlackCount == -1) { + totalBlackCount = currentBlackCount; + } else if (currentBlackCount != totalBlackCount) { + return false; + } + return true; + } + + // 3. У красного узла оба потомка должны быть чёрными + if (node->colour == Colour::Red) { + if (node->left_child->colour != Colour::Black || node->right_child->colour != Colour::Black) { + return false; + } + } + + int newBlackCount = currentBlackCount + (node->colour == Colour::Black ? 1 : 0); + return IsRedBlackTreeValid(node->left_child, newBlackCount, totalBlackCount) && + IsRedBlackTreeValid(node->right_child, newBlackCount, totalBlackCount); +} + +void RedBlackTree::Transplant(Node* u, Node* v) { + if (u->parent == nil) { + root = v; + } else if (u == u->parent->left_child) { + u->parent->left_child = v; + } else { + u->parent->right_child = v; + } + v->parent = u->parent; +} + +RedBlackTree::Node* RedBlackTree::Minimum(Node* node) { + while (node->left_child != nil) { + node = node->left_child; + } + return node; +} + +void RedBlackTree::FixDel(Node* x) { + while (x != root && x->colour == Colour::Black) { + if (x == x->parent->left_child) { + Node* w = x->parent->right_child; + // Случай 1: Брат x красный + if (w->colour == Colour::Red) { + w->colour = Colour::Black; + x->parent->colour = Colour::Red; + LeftRotate(x->parent); + w = x->parent->right_child; + } + // Случай 2: Оба ребёнка брата чёрные + if (w->left_child->colour == Colour::Black && w->right_child->colour == Colour::Black) { + w->colour = Colour::Red; + x = x->parent; + } else { + // Случай 3: Правый ребёнок брата чёрный + if (w->right_child->colour == Colour::Black) { + w->left_child->colour = Colour::Black; + w->colour = Colour::Red; + RightRotate(w); + w = x->parent->right_child; + } + // Случай 4: Правый ребёнок брата красный + w->colour = x->parent->colour; + x->parent->colour = Colour::Black; + w->right_child->colour = Colour::Black; + LeftRotate(x->parent); + x = root; + } + } else { + // Симметричный случай (x — правый ребёнок) + Node* w = x->parent->left_child; + if (w->colour == Colour::Red) { + w->colour = Colour::Black; + x->parent->colour = Colour::Red; + RightRotate(x->parent); + w = x->parent->left_child; + } + if (w->right_child->colour == Colour::Black && w->left_child->colour == Colour::Black) { + w->colour = Colour::Red; + x = x->parent; + } else { + if (w->left_child->colour == Colour::Black) { + w->right_child->colour = Colour::Black; + w->colour = Colour::Red; + LeftRotate(w); + w = x->parent->left_child; + } + w->colour = x->parent->colour; + x->parent->colour = Colour::Black; + w->left_child->colour = Colour::Black; + RightRotate(x->parent); + x = root; + } + } + } + x->colour = Colour::Black; +} + +void RedBlackTree::Del(int key) { + Node* z = Search(key); + if (z == nil) return; + + Node* y = z; + Node* x; + Colour yOriginalColor = y->colour; + + // Случай 1: У z нет левого потомка + if (z->left_child == nil) { + x = z->right_child; + Transplant(z, z->right_child); + } + // Случай 2: У z нет правого потомка + else if (z->right_child == nil) { + x = z->left_child; + Transplant(z, z->left_child); + } + // Случай 3: У z есть оба потомка + else { + y = Minimum(z->right_child); + yOriginalColor = y->colour; + x = y->right_child; + + if (y->parent == z) { + x->parent = y; + } else { + Transplant(y, y->right_child); + y->right_child = z->right_child; + y->right_child->parent = y; + } + + Transplant(z, y); + y->left_child = z->left_child; + y->left_child->parent = y; + y->colour = z->colour; + } + + delete z; + + if (yOriginalColor == Colour::Black) { + FixDel(x); + } +} \ No newline at end of file diff --git a/task_07/src/red_black_tree.hpp b/task_07/src/red_black_tree.hpp new file mode 100644 index 0000000..1aff3a5 --- /dev/null +++ b/task_07/src/red_black_tree.hpp @@ -0,0 +1,65 @@ +#pragma once +enum class Colour{ Red = 0, Black }; + +class RedBlackTree { +private: + struct Node { + int key; + Colour colour; + Node *left_child; + Node *right_child; + Node *parent; + + Node(int key, Colour colour, Node *left_child, Node *right_child, Node *parent = nullptr) + : key(key), colour(colour), left_child(left_child), right_child(right_child), parent(parent) {} + + bool operator!=(const Node &other) const { + return this->key != other.key; + } + }; + + Node *root; + Node *nil; + + bool IsRedBlackTreeValid(Node* node, int blackCount, int& currentBlackCount) const; + bool Contains(Node* node, int key) const; + + void Transplant(Node* u, Node* v); + Node* Minimum(Node* node); + + void Clear(Node* node) { + if (node == nil || node == nullptr) return; + + Clear(node->left_child); + Clear(node->right_child); + + if (node != nil) { + delete node; + } + } + +public: + RedBlackTree() { + nil = new Node(0, Colour::Black, nullptr, nullptr); + nil->left_child = nil; + nil->right_child = nil; + nil->parent = nil; + root = nil; + } + + ~RedBlackTree() { + Clear(root); + delete nil; + } + + void Insert(int key); + void Del(int key); + void FixInsert(Node *z); + void FixDel(Node *x); + void LeftRotate(Node *x); + void RightRotate(Node *y); + Node* Search(int key); + + bool Contains(int key) const; + bool IsValid() const; +}; \ No newline at end of file diff --git a/task_07/src/test.cpp b/task_07/src/test.cpp index 5e11617..07b2492 100644 --- a/task_07/src/test.cpp +++ b/task_07/src/test.cpp @@ -1,6 +1,60 @@ - #include +#include "red_black_tree.hpp" + +class RedBlackTreeTest : public ::testing::Test { +protected: + RedBlackTree tree; + + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(RedBlackTreeTest, InsertMaintainsRedBlackProperties) { + tree.Insert(10); + tree.Insert(20); + tree.Insert(5); + EXPECT_TRUE(tree.IsValid()); +} + +TEST_F(RedBlackTreeTest, ContainsReturnsCorrectResults) { + tree.Insert(10); + tree.Insert(20); + tree.Insert(5); -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + EXPECT_TRUE(tree.Contains(10)); + EXPECT_TRUE(tree.Contains(20)); + EXPECT_TRUE(tree.Contains(5)); + EXPECT_FALSE(tree.Contains(15)); } + +TEST_F(RedBlackTreeTest, DeleteMaintainsRedBlackProperties) { + tree.Insert(10); + tree.Insert(20); + tree.Insert(5); + tree.Insert(15); + + tree.Del(15); + EXPECT_FALSE(tree.Contains(15)); + EXPECT_TRUE(tree.IsValid()); + + tree.Del(10); + EXPECT_FALSE(tree.Contains(10)); + EXPECT_TRUE(tree.IsValid()); +} + +TEST_F(RedBlackTreeTest, EmptyTreeIsValid) { + EXPECT_TRUE(tree.IsValid()); + EXPECT_FALSE(tree.Contains(10)); +} + +TEST_F(RedBlackTreeTest, HandlesDuplicateInsertions) { + tree.Insert(10); + tree.Insert(10); + EXPECT_TRUE(tree.IsValid()); + EXPECT_TRUE(tree.Contains(10)); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/task_08/src/hash_table.hpp b/task_08/src/hash_table.hpp new file mode 100644 index 0000000..164b223 --- /dev/null +++ b/task_08/src/hash_table.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include +#include + +template +class Hashtable final +{ +private: + struct Node + { + bool full; + T value; + Node(T value) : full(true), value(value) {} + }; + + size_t buffer_size = 64; + size_t size = 0; + size_t active_size = 0; + std::vector buffer{buffer_size, nullptr}; + + std::function beginHash, stepHash; + +public: + Hashtable(std::function beginHash, std::function stepHash) : beginHash(beginHash), stepHash(stepHash) {}; + + size_t Size() const { return size; } + size_t ActiveSize() const { return active_size; } + size_t BufferSize() const { return buffer_size; } + + bool Find(T value) + { + size_t begin = beginHash(value) % buffer_size; + size_t step = stepHash(value) % (buffer_size - 1) + 1; + + for (size_t i = 0; i < buffer_size; ++i) + { + if (buffer[begin] == nullptr) return false; + if (buffer[begin]->full && buffer[begin]->value == value) return true; + begin = (begin + step) % buffer_size; + } + return false; + } + + void Resize() + { + buffer_size *= 2; + std::vector old_buffer(buffer_size, nullptr); + std::swap(buffer, old_buffer); + size = 0; + active_size = 0; + + for (size_t i = 0; i < old_buffer.size(); ++i) + { + if (old_buffer[i] != nullptr && old_buffer[i]->full) + { + Add(old_buffer[i]->value); + delete old_buffer[i]; + } + } + } + + void Rehash() + { + std::vector old_buffer(buffer_size, nullptr); + std::swap(buffer, old_buffer); + size = 0; + active_size = 0; + + for (size_t i = 0; i < old_buffer.size(); ++i) + { + if (old_buffer[i] != nullptr && old_buffer[i]->full) + { + Add(old_buffer[i]->value); + delete old_buffer[i]; + } + } + } + + bool Add(T value) + { + if (active_size + 1 > int(0.75 * buffer_size)) Resize(); + if (size > 2 * active_size) Rehash(); + + size_t begin = beginHash(value) % buffer_size; + size_t step = stepHash(value) % (buffer_size - 1) + 1; + + size_t first_empty = buffer_size; + for (size_t i = 0; i < buffer_size; ++i) + { + if (buffer[begin] == nullptr) + { + first_empty = begin; + break; + } + if (buffer[begin]->full && buffer[begin]->value == value) return false; + if (!buffer[begin]->full && first_empty == buffer_size) first_empty = begin; + begin = (begin + step) % buffer_size; + } + + if (first_empty != buffer_size) + { + if (buffer[first_empty] == nullptr) + { + buffer[first_empty] = new Node(value); + active_size++; + } + else + { + buffer[first_empty]->value = value; + buffer[first_empty]->full = true; + } + size++; + return true; + } + return false; + } + + bool Remove(T value) + { + size_t begin = beginHash(value) % buffer_size; + size_t step = stepHash(value) % (buffer_size - 1) + 1; + + for (size_t i = 0; i < buffer_size; ++i) + { + if (buffer[begin] == nullptr) return false; + if (buffer[begin]->full && buffer[begin]->value == value) + { + buffer[begin]->full = false; + size--; + return true; + } + begin = (begin + step) % buffer_size; + } + return false; + } +}; \ No newline at end of file diff --git a/task_08/src/main.cpp b/task_08/src/main.cpp index 0e4393b..e01b907 100644 --- a/task_08/src/main.cpp +++ b/task_08/src/main.cpp @@ -1,3 +1,61 @@ +int main() { return 0; } +/* +#include #include -int main() { return 0; } +using namespace std; + +int main() { + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int n, m; + cin >> n >> m; + + vector> table(n, vector(m)); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + cin >> table[i][j]; + } + } + + // Для каждого столбца создаем массив, где отмечаем, отсортированы ли строки i и i+1 + vector> sorted_cols(m, vector(n - 1)); + for (int j = 0; j < m; ++j) { + for (int i = 0; i < n - 1; ++i) { + sorted_cols[j][i] = (table[i][j] <= table[i + 1][j]); + } + } + + // Для каждого столбца создаем массив префиксных сумм + vector> prefix(m, vector(n)); + for (int j = 0; j < m; ++j) { + prefix[j][0] = 0; + for (int i = 1; i < n; ++i) { + prefix[j][i] = prefix[j][i - 1] + (sorted_cols[j][i - 1] ? 1 : 0); + } + } + + int k; + cin >> k; + while (k--) { + int l, r; + cin >> l >> r; + l--; r--; // Переводим в 0-индексацию + + bool found = false; + for (int j = 0; j < m; ++j) { + // Проверяем, что все пары между l и r-1 отсортированы + int sum = prefix[j][r] - prefix[j][l]; + if (sum == r - l) { + found = true; + break; + } + } + + cout << (found ? "Yes" : "No") << '\n'; + } + + return 0; +} +*/ \ No newline at end of file diff --git a/task_08/src/test.cpp b/task_08/src/test.cpp index 5e11617..5fc0daf 100644 --- a/task_08/src/test.cpp +++ b/task_08/src/test.cpp @@ -1,6 +1,105 @@ - #include +#include "hash_table.hpp" + +size_t simple_begin_hash(int value) { + return value; +} -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +size_t simple_step_hash(int value) { + return value + 1; } + +class HashTableTest : public ::testing::Test { +protected: + void SetUp() override { + table = new Hashtable(simple_begin_hash, simple_step_hash); + } + + void TearDown() override { + delete table; + } + + Hashtable* table; +}; + +TEST_F(HashTableTest, InitialState) { + EXPECT_EQ(0, table->Size()); + EXPECT_EQ(0, table->ActiveSize()); + EXPECT_EQ(64, table->BufferSize()); +} + +TEST_F(HashTableTest, AddAndFind) { + EXPECT_TRUE(table->Add(42)); + EXPECT_TRUE(table->Find(42)); + EXPECT_EQ(1, table->Size()); + EXPECT_EQ(1, table->ActiveSize()); +} + +TEST_F(HashTableTest, AddDuplicate) { + EXPECT_TRUE(table->Add(42)); + EXPECT_FALSE(table->Add(42)); + EXPECT_EQ(1, table->Size()); +} + +TEST_F(HashTableTest, Remove) { + table->Add(42); + EXPECT_TRUE(table->Remove(42)); + EXPECT_FALSE(table->Find(42)); + EXPECT_EQ(0, table->Size()); + EXPECT_EQ(1, table->ActiveSize()); +} + +TEST_F(HashTableTest, RemoveNonExistent) { + EXPECT_FALSE(table->Remove(42)); +} + +TEST_F(HashTableTest, HandleCollisions) { + auto collision_hash = [](int) { return 0; }; + Hashtable coll_table(collision_hash, [](int) { return 1; }); + + EXPECT_TRUE(coll_table.Add(1)); + EXPECT_TRUE(coll_table.Add(2)); + EXPECT_TRUE(coll_table.Find(1)); + EXPECT_TRUE(coll_table.Find(2)); +} + +TEST_F(HashTableTest, ResizeWhenFull) { + Hashtable small_table(simple_begin_hash, simple_step_hash); + + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(small_table.Add(i)); + } + + for (int i = 0; i < 50; ++i) { + EXPECT_TRUE(small_table.Find(i)); + } +} + +TEST_F(HashTableTest, RehashWhenManyDeleted) { + for (int i = 0; i < 100; ++i) { + table->Add(i); + } + + for (int i = 0; i < 50; ++i) { + table->Remove(i); + } + + for (int i = 50; i < 100; ++i) { + EXPECT_TRUE(table->Find(i)); + } + for (int i = 0; i < 50; ++i) { + EXPECT_FALSE(table->Find(i)); + } +} + +TEST_F(HashTableTest, AddAfterRemove) { + table->Add(42); + table->Remove(42); + EXPECT_TRUE(table->Add(42)); + EXPECT_TRUE(table->Find(42)); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/task_09/src/main.cpp b/task_09/src/main.cpp index c3aeba4..1ff2d9f 100644 --- a/task_09/src/main.cpp +++ b/task_09/src/main.cpp @@ -1 +1,39 @@ -int main() { return 0; } \ No newline at end of file +#include +#include +#include "solution.hpp" + +using namespace std; + +int main() { + ios_base::sync_with_stdio(false); + cin.tie(nullptr); + + int n, m; + cin >> n >> m; + + vector> table(n, vector(m)); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + cin >> table[i][j]; + } + } + + auto segment_start = ComputeSegmentStart(table, n, m); + auto row_max_good = ComputeRowMaxGood(segment_start, n, m); + + int k; + cin >> k; + while (k--) { + int l, r; + cin >> l >> r; + l--; r--; + + if (CanSort(row_max_good, l, r)) { + cout << "Yes\n"; + } else { + cout << "No\n"; + } + } + + return 0; +} \ No newline at end of file diff --git a/task_09/src/solution.hpp b/task_09/src/solution.hpp new file mode 100644 index 0000000..cd25996 --- /dev/null +++ b/task_09/src/solution.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +using namespace std; + +inline vector> ComputeColSorted(const vector>& table, int n, int m) { + vector> col_sorted(m, vector(n)); + for (int j = 0; j < m; ++j) { + col_sorted[j][0] = true; + for (int i = 1; i < n; ++i) { + col_sorted[j][i] = col_sorted[j][i-1] && (table[i-1][j] <= table[i][j]); + } + } + return col_sorted; +} + +inline vector> ComputeSegmentStart(const vector>& table, int n, int m) { + vector> segment_start(m, vector(n)); + for (int j = 0; j < m; ++j) { + segment_start[j][0] = 0; + for (int i = 1; i < n; ++i) { + if (table[i-1][j] <= table[i][j]) { + segment_start[j][i] = segment_start[j][i-1]; + } else { + segment_start[j][i] = i; + } + } + } + return segment_start; +} + +inline vector ComputeRowMaxGood(const vector>& segment_start, int n, int m) { + vector row_max_good(n); + for (int i = 0; i < n; ++i) { + int max_good = -1; + for (int j = 0; j < m; ++j) { + if (segment_start[j][i] <= i) { + max_good = max(max_good, segment_start[j][i]); + } + } + row_max_good[i] = max_good; + } + return row_max_good; +} + +inline bool CanSort(const vector& row_max_good, int l, int r) { + if (l >= r) { + return true; + } + return row_max_good[r] <= l; +} diff --git a/task_09/src/test.cpp b/task_09/src/test.cpp index a42caa4..a88e327 100644 --- a/task_09/src/test.cpp +++ b/task_09/src/test.cpp @@ -1,23 +1,40 @@ #include +#include "solution.hpp" -#include -TEST(CanReachNonDecreasingSegment, 1) { - // ASSERT_EQ(SolveFunction(5, 4, 6, - // std::vector>{{1, 2, 3, 5}, - // {3, 1, 3, 2}, - // {4, 5, 2, 3}, - // {5, 5, 3, 2}, - // {4, 4, 3, 4}}, - // std::vector>{ - // {1, 1}, {2, 5}, {4, 5}, {3, 5}, {1, 3}, {1, - // 5}}), - // (std::vector{"Yes", "No", "Yes", "Yes", "Yes", - // "No"})); +TEST(TableSortTest, SingleColumnSorted) { + vector> table = { + {1}, + {2}, + {3} + }; + int n = 3, m = 1; + + auto segment_start = ComputeSegmentStart(table, n, m); + auto row_max_good = ComputeRowMaxGood(segment_start, n, m); + + EXPECT_TRUE(CanSort(row_max_good, 0, 2)); + EXPECT_TRUE(CanSort(row_max_good, 1, 2)); } -TEST(CanReachNonDecreasingSegment, 2) { - // ASSERT_EQ(SolveFunction(1, 1, 1, std::vector>{{1, 1}}, - // std::vector>{{1, 1}}), - // (std::vector{"Yes"})); +TEST(TableSortTest, SingleColumnUnsorted) { + vector> table = { + {3}, + {2}, + {1} + }; + int n = 3, m = 1; + + auto segment_start = ComputeSegmentStart(table, n, m); + auto row_max_good = ComputeRowMaxGood(segment_start, n, m); + + EXPECT_FALSE(CanSort(row_max_good, 0, 2)); + EXPECT_FALSE(CanSort(row_max_good, 1, 2)); + EXPECT_TRUE(CanSort(row_max_good, 2, 2)); } + + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file