-
Notifications
You must be signed in to change notification settings - Fork 26
hw_algorithms #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
hw_algorithms #16
Changes from all commits
031aa65
8a713aa
7d5ace0
740eff2
e90eb9b
8ed0665
fe57707
9137a97
0a52b29
6083a1d
ab91336
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <iostream> | ||
| #include <vector> | ||
|
|
||
| class FenwickTree { | ||
| private: | ||
| std::vector<int> 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<int> 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 | ||
| ``` | ||
|
|
||
| ### Применение | ||
| Дерево Фенвика часто используется в задачах: | ||
| - Подсчет суммы на префиксе или отрезке. | ||
| - Решение задач на инверсии в перестановках. | ||
| - Оптимизация динамического программирования. | ||
|
|
||
| Это мощный инструмент для задач, где важна скорость работы с префиксными суммами |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,21 @@ | ||
| #include "solution.hpp" | ||
| #include <iostream> | ||
|
|
||
| int main() { return 0; } | ||
| int main() { | ||
|
LostPointer marked this conversation as resolved.
|
||
| int n; | ||
| int c; | ||
| cin>>n; | ||
| vector<int> a(n); | ||
| cin>>c; | ||
| for (int i=0; i<n; i++) | ||
| { | ||
| cin>>a[i]; | ||
| } | ||
| Result result = Solve(n, c, a); | ||
|
|
||
| if (result.finded) | ||
| cout << a[result.left] << ' ' << a[result.right] << endl; | ||
|
LostPointer marked this conversation as resolved.
|
||
| else | ||
| cout << "Не удалось найти ответ!" << endl; | ||
|
|
||
| return 0; } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #pragma once | ||
|
|
||
| #include <vector> | ||
| using namespace std; | ||
|
|
||
| struct Result | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. норм решение, но лучше обернуть все в std::optional, станет красивее |
||
| { | ||
| bool finded = false; | ||
| int left = 0, right = 0; | ||
| }; | ||
|
|
||
| inline Result Solve(int n, int c, const vector<int>& a) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. давай не n тут не нужна) просто сделай
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. и лучше сделай еще один файл cpp и туда реализацию перекинь) и имя файлу лучше придумать по лучше |
||
| { | ||
| if (a.size() != n) return {}; | ||
|
|
||
| int left = 0; | ||
| int right = n-1; | ||
| while(left<right) | ||
| { | ||
| if (a[left]+a[right]<c) {++left; continue;} | ||
| if (a[left]+a[right]>c) {--right; continue;} | ||
| if (a[left]+a[right]==c) return {true, a[left], a[right]}; | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,93 @@ | ||
| #include <vector> | ||
| #include <gtest/gtest.h> | ||
| #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<int> 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<int> nums = {1, 2, 3, 4}; | ||
| int target = 8; | ||
| Result res = Solve(nums.size(), target, nums); | ||
|
|
||
| EXPECT_FALSE(res.finded); | ||
| } | ||
|
|
||
| TEST_F(TwoSumTest, AllElementsSame) { | ||
| vector<int> 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<int> 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<int> nums = {}; | ||
| int target = 5; | ||
| Result res = Solve(nums.size(), target, nums); | ||
|
|
||
| EXPECT_FALSE(res.finded); | ||
| } | ||
|
|
||
| TEST_F(TwoSumTest, SingleElementArray) { | ||
| vector<int> nums = {5}; | ||
| int target = 5; | ||
| Result res = Solve(nums.size(), target, nums); | ||
|
|
||
| EXPECT_FALSE(res.finded); | ||
| } | ||
|
|
||
| TEST_F(TwoSumTest, TargetTooSmall) { | ||
| vector<int> nums = {10, 20, 30}; | ||
| int target = 5; | ||
| Result res = Solve(nums.size(), target, nums); | ||
|
|
||
| EXPECT_FALSE(res.finded); | ||
| } | ||
|
|
||
| TEST_F(TwoSumTest, TargetTooLarge) { | ||
| vector<int> nums = {1, 2, 3}; | ||
| int target = 10; | ||
| Result res = Solve(nums.size(), target, nums); | ||
|
|
||
| EXPECT_FALSE(res.finded); | ||
| } | ||
|
|
||
| TEST_F(TwoSumTest, MultipleSolutions) { | ||
| vector<int> 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(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| #include <iostream> | ||
| #include <vector> | ||
| using namespace std; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. второй задачи нет(
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я не до конца поняла, нужно было её делать или нет, т.к. в исходном файле уже было решение этой задачи
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. там реализация наивная и не оптимальная) нужно написать заново не на stack'е |
||
|
|
||
|
|
||
| int main() { return 0; } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| #include <iostream> | ||
| #include <vector> | ||
| using namespace std; | ||
|
|
||
| int main() { return 0; } | ||
|
|
||
|
|
||
| int main() { return 0; } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #pragma once | ||
| #include <vector> | ||
| #include <stack> | ||
|
|
||
| using namespace std; | ||
|
|
||
| inline vector<int> DailyTemp(const vector<int>& temp){ | ||
| int n = temp.size(); | ||
| vector<int> res(n, 0); | ||
| stack<int> s; | ||
|
|
||
| for(int i = 0; i<n; ++i) | ||
| { | ||
| while(!s.empty() && temp[i]>temp[s.top()]) | ||
| { | ||
| int prev_d = s.top(); | ||
| s.pop(); | ||
| res[prev_d] = i - prev_d; | ||
| } | ||
| s.push(i); | ||
| } | ||
| return res; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,46 @@ | ||
|
|
||
| #include <gtest/gtest.h> | ||
| #include <vector> | ||
| #include "solution.hpp" | ||
|
|
||
| using namespace std; | ||
|
|
||
| TEST(DailyTempTest, BasicTest) { | ||
| vector<int> temps = {73, 74, 75, 71, 69, 72, 76, 73}; | ||
| vector<int> expected = {1, 1, 4, 2, 1, 1, 0, 0}; | ||
| EXPECT_EQ(DailyTemp(temps), expected); | ||
| } | ||
|
|
||
| TEST(DailyTempTest, NoWarming) { | ||
| vector<int> temps = {80, 75, 70, 65, 60}; | ||
| vector<int> expected = {0, 0, 0, 0, 0}; | ||
| EXPECT_EQ(DailyTemp(temps), expected); | ||
| } | ||
|
|
||
| #include "topology_sort.hpp" | ||
| TEST(DailyTempTest, ImmediateWarming) { | ||
| vector<int> temps = {50, 60, 70, 80, 90}; | ||
| vector<int> expected = {1, 1, 1, 1, 0}; | ||
| EXPECT_EQ(DailyTemp(temps), expected); | ||
| } | ||
|
|
||
| TEST(DailyTempTest, EmptyInput) { | ||
| vector<int> temps = {}; | ||
| vector<int> expected = {}; | ||
| EXPECT_EQ(DailyTemp(temps), expected); | ||
| } | ||
|
|
||
| TEST(TopologySort, Simple) { | ||
| ASSERT_EQ(1, 1); // Stack [] | ||
| TEST(DailyTempTest, SingleDay) { | ||
| vector<int> temps = {100}; | ||
| vector<int> expected = {0}; | ||
| EXPECT_EQ(DailyTemp(temps), expected); | ||
| } | ||
|
|
||
| TEST(DailyTempTest, MixedWarming) { | ||
| vector<int> temps = {30, 40, 20, 25, 35, 10}; | ||
| vector<int> 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(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
удали из домашки плиз теорию