diff --git a/.gitignore b/.gitignore index 41f1b1bd..1189e2d1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ _deps .cache/* .vscode/* -build/* \ No newline at end of file +build/* +.clang-format +.gitignore diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 0e4393ba..bebfe295 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1,3 +1,23 @@ #include -int main() { return 0; } +#include "searching_sum.hpp" + +/* +Формат ввода: + В первой строке ввода через пробел подаётся 2 целых числа: + sum - заданное число, n - длина массива чисел + В следующей строке подаются n целых чисел - элемента массива + +Формат вывода: + В одной строке через пробел выводятся два числа - индексы элементов данного + массива, дающие в сумме sum +*/ +int main() { + int sum = 0, n = 0; + std::cin >> sum >> n; + std::vector arr(n); + for (int i = 0; i < n; ++i) std::cin >> arr[i]; + std::pair answer = SearchingSum(sum, arr); + std::cout << answer.first << " " << answer.second << std::endl; + return 0; +} diff --git a/task_01/src/searching_sum.cpp b/task_01/src/searching_sum.cpp new file mode 100644 index 00000000..a1de8c21 --- /dev/null +++ b/task_01/src/searching_sum.cpp @@ -0,0 +1,15 @@ +#include "searching_sum.hpp" + +/* +Алгоритм для нахождения в массиве чисел, дающих в сумме заданное число +Ассимптотика алгоритма - O(n), затраты по памяти - O(n) +*/ +std::pair SearchingSum(int sum, std::vector arr) { + std::pair ans = {0, arr.size() - 1}; + while (ans.first < ans.second) { + if (arr[ans.first] + arr[ans.second] == sum) return ans; + if (arr[ans.first] + arr[ans.second] < sum) ++ans.first; + if (arr[ans.first] + arr[ans.second] > sum) --ans.second; + } + return {-1, -1}; +} \ No newline at end of file diff --git a/task_01/src/searching_sum.hpp b/task_01/src/searching_sum.hpp new file mode 100644 index 00000000..34338f20 --- /dev/null +++ b/task_01/src/searching_sum.hpp @@ -0,0 +1,9 @@ +#ifndef SEARCHING_SUM +#define SEARCHING_SUM + +#include +#include + +std::pair SearchingSum(int sum, std::vector arr); + +#endif // SEARCHING_SUM \ No newline at end of file diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index ef5a86ae..689364eb 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,8 +1,36 @@ #include -#include "topology_sort.hpp" +#include "searching_sum.hpp" -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(SearchingSum, Simple) { + int sum1 = 10; + std::vector arr1 = {1, 3, 4, 7, 14}; + std::pair ans1 = {1, 3}; + ASSERT_EQ(ans1, SearchingSum(sum1, arr1)); + + int sum2 = 19; + std::vector arr2 = {1, 2, 5, 5, 6, 7, 8, 9, 14}; + std::pair ans2 = {2, 8}; + ASSERT_EQ(ans2, SearchingSum(sum2, arr2)); + + int sum3 = 8; + std::vector arr3 = {}; + std::pair ans3 = {-1, -1}; + ASSERT_EQ(ans3, SearchingSum(sum3, arr3)); + + int sum4 = -1; + std::vector arr4 = {-7, -5, 0, 1, 3, 4, 6}; + std::pair ans4 = {0, 6}; + ASSERT_EQ(ans4, SearchingSum(sum4, arr4)); + + int sum5 = 0; + std::vector arr5 = {1, 2, 5, 5, 6, 7, 8, 9, 14}; + std::pair ans5 = {-1, -1}; + ASSERT_EQ(ans5, SearchingSum(sum5, arr5)); + + int sum6 = 2; + std::vector arr6 = {2}; + std::pair ans6 = {-1, -1}; + ASSERT_EQ(ans6, SearchingSum(sum6, arr6)); } diff --git a/task_02/src/stack.cpp b/task_02/src/stack.cpp deleted file mode 100644 index 8ca89902..00000000 --- a/task_02/src/stack.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "stack.hpp" - -#include - -void Stack::Push(int value) { data_.push(value); } - -int Stack::Pop() { - auto result = data_.top(); - data_.pop(); - return result; -} - -void MinStack::Push(int value) { data_.push_back(value); } - -int MinStack::Pop() { - auto result = data_.back(); - data_.pop_back(); - return result; -} - -int MinStack::GetMin() { return *std::min_element(data_.begin(), data_.end()); } \ No newline at end of file diff --git a/task_02/src/stack.hpp b/task_02/src/stack.hpp index 138ec40f..2b481331 100644 --- a/task_02/src/stack.hpp +++ b/task_02/src/stack.hpp @@ -1,23 +1,99 @@ #pragma once -#include -#include +#include +#include +/// @brief Элемент стека +template +struct Element { + explicit Element(T data, const std::shared_ptr& next) + : data_(data), next_(next) {} + + T data_; + std::shared_ptr next_; +}; + +/// @brief Cтек +/// @tparam Т Тип данных +template class Stack { public: - void Push(int value); - int Pop(); + explicit Stack() : top_(nullptr), size_{0} {} + + std::size_t Size() const; + virtual void Push(T data); + virtual T Pop(); + virtual T Top() const; private: - std::stack data_; + std::size_t size_; + std::shared_ptr> top_; }; -class MinStack { +/// @brief Стек с минимумом +/// @tparam T Тип данных +template +class MinStack : public Stack { public: - void Push(int value); - int Pop(); - int GetMin(); + explicit MinStack() : Stack(), minimums_stack_() {} + + void Push(T data) override; + T Pop() override; + T GetMin() const; private: - std::vector data_; + Stack minimums_stack_; + std::size_t size_; + std::shared_ptr> top_; }; + +template +std::size_t Stack::Size() const { + return size_; +} + +template +void Stack::Push(T data) { + top_ = std::make_shared>(data, top_); + size_++; +} + +template +T Stack::Pop() { + if (size_ == 0) throw std::runtime_error("Stack is empty"); + T removed_value = top_->data_; + top_ = std::move(top_->next_); + size_--; + return removed_value; +} + +template +T Stack::Top() const { + if (size_ == 0) throw std::runtime_error("Stack is empty"); + return top_->data_; +} + +template +void MinStack::Push(T data) { + top_ = std::make_shared>(data, top_); + size_++; + if (minimums_stack_.Size() > 0 && data > minimums_stack_.Top()) + minimums_stack_.Push(minimums_stack_.Top()); + else + minimums_stack_.Push(data); +} + +template +T MinStack::Pop() { + if (size_ == 0) throw std::runtime_error("Stack is empty"); + T removed_value = top_->data_; + top_ = std::move(top_->next_); + size_--; + minimums_stack_.Pop(); + return removed_value; +} + +template +T MinStack::GetMin() const { + return minimums_stack_.Top(); +} \ No newline at end of file diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 54e7ce90..cb838fd1 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,4 +1,3 @@ - #include #include @@ -6,7 +5,7 @@ #include "stack.hpp" TEST(StackTest, Simple) { - Stack stack; + Stack stack; stack.Push(1); // Stack [1] ASSERT_EQ(stack.Pop(), 1); // Stack [] stack.Push(1); // Stack [1] @@ -22,7 +21,7 @@ TEST(StackTest, Simple) { } TEST(MinStackTest, Simple) { - MinStack stack; + MinStack stack; stack.Push(1); // Stack [1] ASSERT_EQ(stack.GetMin(), 1); ASSERT_EQ(stack.Pop(), 1); // Stack [] diff --git a/task_03/src/days_amount_before_warming.cpp b/task_03/src/days_amount_before_warming.cpp new file mode 100644 index 00000000..5838728e --- /dev/null +++ b/task_03/src/days_amount_before_warming.cpp @@ -0,0 +1,19 @@ +#include "days_amount_before_warming.h" + +#include + +std::vector DaysAmountBeforeWarming( + std::vector temperatures) { + std::vector result(temperatures.size()); + std::stack remaining_days; + for (std::size_t i = 0; i < temperatures.size(); ++i) { + result[i] = 0; + while (!remaining_days.empty() && + temperatures[i] > temperatures[remaining_days.top()]) { + result[remaining_days.top()] = i - remaining_days.top(); + remaining_days.pop(); + } + remaining_days.push(i); + } + return result; +} \ No newline at end of file diff --git a/task_03/src/days_amount_before_warming.h b/task_03/src/days_amount_before_warming.h new file mode 100644 index 00000000..6860187b --- /dev/null +++ b/task_03/src/days_amount_before_warming.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::vector DaysAmountBeforeWarming(std::vector temperatures); \ No newline at end of file diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index ef5a86ae..05229f2a 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,8 +1,31 @@ - #include -#include "topology_sort.hpp" +#include "days_amount_before_warming.h" + +TEST(Temperature, Simple) { + std::vector days_data_1 = DaysAmountBeforeWarming({1, 2, 3, 4, 5}); + std::vector answer_1 = {1, 1, 1, 1, 0}; + + ASSERT_EQ(days_data_1, answer_1); + + std::vector days_data_2 = DaysAmountBeforeWarming({5}); + std::vector answer_2 = {0}; + + ASSERT_EQ(days_data_2, answer_2); + + std::vector days_data_3 = DaysAmountBeforeWarming({}); + std::vector answer_3 = {}; + + ASSERT_EQ(days_data_3, answer_3); + + std::vector days_data_4 = + DaysAmountBeforeWarming({-1, -1, 2, 2, -5, 3, 4, 1}); + std::vector answer_4 = {2, 1, 3, 2, 1, 1, 0, 0}; + + ASSERT_EQ(days_data_4, answer_4); + + std::vector days_data_5 = DaysAmountBeforeWarming({6, 5, 4, 3, 2, 1}); + std::vector answer_5 = {0, 0, 0, 0, 0, 0}; -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + ASSERT_EQ(days_data_5, answer_5); } diff --git a/task_03/src/topology_sort.cpp b/task_03/src/topology_sort.cpp deleted file mode 100644 index e53f670c..00000000 --- a/task_03/src/topology_sort.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "topology_sort.hpp" diff --git a/task_03/src/topology_sort.hpp b/task_03/src/topology_sort.hpp deleted file mode 100644 index 6f70f09b..00000000 --- a/task_03/src/topology_sort.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/task_04/src/heap.hpp b/task_04/src/heap.hpp new file mode 100644 index 00000000..c0bdcac8 --- /dev/null +++ b/task_04/src/heap.hpp @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include + +/** + * @brief Двоичная куча на std::vector с доступом к минимальному элементу + * @tparam T: Тип данных в двоичной кучи + */ +template +class BinaryMinHeap { + public: + BinaryMinHeap() = default; + BinaryMinHeap(const std::vector& data) { + for (const auto& elem : data) Add(elem); + } + + BinaryMinHeap(const std::initializer_list& data) { + for (const auto& elem : data) Add(elem); + } + + /** + * @brief Индексация элементов кучи + * @param index + * @return T: Значение элемента + * @throw std::out_of_range: Индекс больше размера кучи + */ + T operator[](std::size_t index) const { + if (index > Size()) throw std::out_of_range("invalid index"); + + return data_[index]; + } + + /** + * @brief Доступ к данным кучи (const &) + * @return const std::vector&: все элементов + */ + const std::vector& Data() const { return data_; } + + /** + * @brief Доступ к данным кучи (const &) + * @return const std::vector&: вектор всех элементов + */ + explicit operator const std::vector&() const { return Data(); } + + /** + * @brief Добавление элемента в кучу + * @param elem: Значение нового элемента + */ + void Add(T elem) { + data_.push_back(elem); + SiftUp(); + } + + /** + * @brief Вырезает корень кучи + * @return T: Значение корня + * @throw std::out_of_range: Куча пуста + */ + T ExtractMin() { + if (Empty()) throw std::out_of_range("empty heap"); + + auto res = Data()[0]; + + // меняем местами минимум с последним + std::swap(data_[0], data_[Size() - 1]); + data_.pop_back(); + SiftDown(); + + return res; + } + + /** + * @brief Возвращает значение корня + * @return T: Значение корня + * @throw std::out_of_range: Куча пуста + */ + T GetMin() const { + if (Empty()) throw std::out_of_range("empty heap"); + + return Data()[0]; + } + + std::size_t Size() const { return data_.size(); } + + bool Empty() const { return Size() == 0; } + + private: + /** + * @brief Возвращает индекс родителя + * @param index: Индекс элемента + * @return std::size_t: Индекс родителя + */ + std::size_t ParentIndex(std::size_t index) const { return (index - 1) / 2; } + + /** + * @brief Возвращает индекс левого ребенка + * @param index: Индекс элемента + * @return std::size_t: Индекс левого ребенка + */ + std::size_t LeftChildIndex(std::size_t index) const { + return (2 * index + 1); + } + + /** + * @brief Возвращает индекс правого ребенка + * @param index: Индекс элемента + * @return std::size_t: Индекс правого ребенка + */ + std::size_t RightChildIndex(std::size_t index) const { + return (2 * index + 2); + } + + /// @brief Корректирует кучу после добавления элемента + void SiftUp() { + // начинаем с конца + std::size_t index = Size() - 1; + + // меняем элемент с родителям местами, пока последний больше + while (index > 0 && data_[ParentIndex(index)] > data_[index]) { + std::swap(data_[index], data_[ParentIndex(index)]); + index = ParentIndex(index); + } + } + + /// @brief Корректирует кучу после удаления элемента + void SiftDown(std::size_t index = 0) { + while (index < Size()) { + std::size_t smallest = index; // индекс кореня поддерева + + // если левый ребенок существует и меньше текущего наименьшего элемента + if (LeftChildIndex(index) < Size() && + data_[LeftChildIndex(index)] < data_[smallest]) { + smallest = LeftChildIndex(index); + } + + // если правый ребенок существует и меньше текущего наименьшего элемента + if (RightChildIndex(index) < Size() && + data_[RightChildIndex(index)] < data_[smallest]) { + smallest = RightChildIndex(index); + } + + // если текущий элемент не меньше своих детей, завершаем итерацию + if (smallest == index) break; + + // меняем местами текущий элемент с наименьшим из детей + std::swap(data_[index], data_[smallest]); + + // переходим вниз по дереву к ребенку, с которым произвели обмен + index = smallest; + } + } + + /// @brief Элементы кучи + std::vector data_; +}; diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617e..93c54fd5 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,162 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include + +#include "heap.hpp" + +TEST(BinaryMinHeap_test, Empty) { + BinaryMinHeap heap; + ASSERT_EQ(heap.Empty(), true); + ASSERT_EQ(heap.Size(), 0); + heap.Add(1); + ASSERT_EQ(heap.Empty(), false); + ASSERT_EQ(heap.Size(), 1); + ASSERT_EQ(heap.ExtractMin(), 1); + EXPECT_THROW(heap.GetMin(), std::out_of_range); + EXPECT_THROW(heap.ExtractMin(), std::out_of_range); +} + +TEST(BinaryMinHeap_test, WithoutDuplicates) { + BinaryMinHeap heap{}; + for (int value : {3, 1, 2, 4, 5}) heap.Add(value); + ASSERT_EQ(heap.ExtractMin(), 1); + ASSERT_EQ(heap.ExtractMin(), 2); + ASSERT_EQ(heap.ExtractMin(), 3); + ASSERT_EQ(heap.ExtractMin(), 4); + ASSERT_EQ(heap.ExtractMin(), 5); +} + +TEST(BinaryMinHeap_test, WithDuplicates) { + BinaryMinHeap heap{}; + for (int value : {6, 7, 7, 3, 3, 3, 1, 2}) heap.Add(value); + ASSERT_EQ(heap.ExtractMin(), 1); + ASSERT_EQ(heap.ExtractMin(), 2); + ASSERT_EQ(heap.ExtractMin(), 3); + ASSERT_EQ(heap.ExtractMin(), 3); + ASSERT_EQ(heap.ExtractMin(), 3); + ASSERT_EQ(heap.ExtractMin(), 6); + ASSERT_EQ(heap.ExtractMin(), 7); + ASSERT_EQ(heap.ExtractMin(), 7); +} + +TEST(BinaryMinHeap_test, InitListConstructor) { + BinaryMinHeap heap{1, 2, 3}; + ASSERT_EQ(heap.ExtractMin(), 1); + ASSERT_EQ(heap.ExtractMin(), 2); + ASSERT_EQ(heap.ExtractMin(), 3); +} + +TEST(BinaryMinHeap_test, EmptyHeapDataTest) { + BinaryMinHeap heap; + EXPECT_TRUE(heap.Data().empty()); + ASSERT_EQ(heap.Data().empty(), heap.Empty()); +} + +TEST(BinaryMinHeap_test, AddElementTest) { + BinaryMinHeap heap; + heap.Add(5); + EXPECT_EQ(heap.Data().size(), 1); + EXPECT_EQ(heap.GetMin(), 5); +} + +TEST(BinaryMinHeap_test, ExtractMinTest) { + BinaryMinHeap heap; + heap.Add(5); + heap.Add(3); + heap.Add(7); + EXPECT_EQ(heap.ExtractMin(), 3); + EXPECT_EQ(heap.Data().size(), 2); +} + +TEST(BinaryMinHeap_test, GetMinTest) { + BinaryMinHeap heap; + heap.Add(5); + heap.Add(3); + heap.Add(7); + EXPECT_EQ(heap.GetMin(), 3); +} + +TEST(BinaryMinHeap_test, MaintainHeapPropertyAfterAdd) { + BinaryMinHeap heap; + heap.Add(5); + heap.Add(3); + heap.Add(7); + heap.Add(2); + heap.Add(6); + EXPECT_EQ(heap.Data()[0], 2); // Минимальный элемент должен быть 2 +} + +TEST(BinaryMinHeap_test, MaintainHeapPropertyAfterExtractMin) { + BinaryMinHeap heap; + heap.Add(5); + heap.Add(3); + heap.Add(7); + heap.Add(2); + heap.Add(6); + heap.ExtractMin(); + EXPECT_EQ(heap.GetMin(), + 3); // После извлечения минимальный элемент должен быть 3 +} + +TEST(BinaryMinHeap_test, AddDuplicateElements) { + BinaryMinHeap heap; + heap.Add(5); + heap.Add(3); + heap.Add(5); + EXPECT_EQ(heap.GetMin(), 3); // Минимальный элемент должен быть 3 +} + +TEST(BinaryMinHeap_test, AddAndRemoveNegativeElements) { + BinaryMinHeap heap; + heap.Add(-5); + heap.Add(3); + heap.Add(-7); + heap.ExtractMin(); + EXPECT_EQ(heap.GetMin(), -5); // Минимальный элемент должен быть -5 +} + +TEST(BinaryMinHeap_test, ExtractMinFromEmptyHeap) { + BinaryMinHeap heap; + ASSERT_THROW(heap.ExtractMin(), std::out_of_range); } + +TEST(BinaryMinHeap_test, AddAndExtractMinLargeData) { + BinaryMinHeap heap; + for (int i = 1000; i > 0; --i) { + heap.Add(i); + } + for (int i = 1; i <= 500; ++i) { + EXPECT_EQ(heap.ExtractMin(), i); + } +} + +TEST(BinaryMinHeap_test, ExtractMinWithDuplicates) { + BinaryMinHeap heap; + heap.Add(5); + heap.Add(3); + heap.Add(3); + heap.Add(7); + heap.Add(2); + EXPECT_EQ(heap.ExtractMin(), 2); + EXPECT_EQ(heap.ExtractMin(), 3); + EXPECT_EQ(heap.ExtractMin(), 3); +} + +TEST(BinaryMinHeap_test, ExtractMinAfterInitializationWithVector) { + std::vector data = {8, 2, 10, 3, 5}; + BinaryMinHeap heap(data); + EXPECT_EQ(heap.ExtractMin(), 2); +} + +TEST(BinaryMinHeap_test, ExtractMinAfterInitializationWithInitializerList) { + BinaryMinHeap heap = {15, 7, 9, 4, 11}; + EXPECT_EQ(heap.ExtractMin(), 4); +} + +TEST(BinaryMinHeap_test, ExtractMinAfterInitializationAndAdd) { + std::vector data = {8, 2, 10, 3, 5}; + BinaryMinHeap heap(data); + heap.Add(4); + heap.Add(1); + EXPECT_EQ(heap.ExtractMin(), 1); +} \ No newline at end of file diff --git a/task_05/src/merge_sort.hpp b/task_05/src/merge_sort.hpp new file mode 100644 index 00000000..d4ed80e3 --- /dev/null +++ b/task_05/src/merge_sort.hpp @@ -0,0 +1,65 @@ +#include + +/** + * @brief Сливает две отсортированные части вектора в один + * @tparam T: Тип элементов вектора + * @param data: Данные двух векторов + * @param left: Индекс начала первого вектора + * @param mid: Индекс конца первого вектора и начала второго вектора + * @param right: Индекс конца второго вектора + */ +template +static void Merge(std::vector& data, std::size_t left, std::size_t mid, + std::size_t right) { + std::size_t index_1 = 0; // индекс для первой части + std::size_t index_2 = 0; // индекс для второй части + + /// @brief Вспомогательный вектор для слияния + std::vector res(right - left); + + // Сливаем два вектора, пока не достигнем конца одного из них + while (left + index_1 < mid && mid + index_2 < right) { + if (data[left + index_1] < data[mid + index_2]) { + // берем элемент из первой части + res[index_1 + index_2] = data[left + index_1]; + index_1++; + + } else { + // берем элемент из второй части + res[index_1 + index_2] = data[mid + index_2]; + index_2++; + } + } + + // Копируем остаток первого вектора, если он не кончился + while (left + index_1 < mid) { + res[index_1 + index_2] = data[left + index_1]; + index_1++; + } + + // Копируем остаток первого вектора, если он не кончился + while (mid + index_2 < right) { + res[index_1 + index_2] = data[mid + index_2]; + index_2++; + } + + // Копируем результат слияния в исходный вектор + for (std::size_t i = 0; i < index_1 + index_2; i++) data[left + i] = res[i]; +} + +/** + * @brief Сортировка слиянием — алгоритм сортировки, использующий O(n) + * дополнительной памяти и работающий за O(nlog(n)) времени + * @tparam T: Тип данных + * @param data: Данные + */ +template +void MergeSort(std::vector& data) { + if (data.empty()) return; + + auto n = data.size(); + + for (std::size_t i = 1; i < n; i *= 2) + for (std::size_t j = 0; j < n - i; j += 2 * i) + Merge(data, j, j + i, std::min(j + 2 * i, n)); +} \ No newline at end of file diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617e..815b9c1b 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,158 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include +#include +#include +#include + +TEST(Sort, Simple) { + std::vector b{5, 3, 4, 1, 2}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, Empty) { + std::vector b; + MergeSort(b); + EXPECT_TRUE(b.empty()); +} + +TEST(Sort, Sorted) { + std::vector b{1, 2, 3, 4, 5}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, Double) { + std::vector b{5.3, 3.1, 4.7, 1.2, 2.8}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, Strings) { + std::vector b{"apple", "banana", "cherry", "date", "elderberry"}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, Duplicates) { + std::vector b{5, 3, 4, 1, 2, 3, 1, 4}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, NegativeNumbers) { + std::vector b{-5, -3, -4, -1, -2}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, Characters) { + std::vector b{'e', 'a', 'c', 'b', 'd'}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +struct TestType { + int value; + auto operator<=>(const TestType& other) const = default; +}; + +TEST(Sort, CustomTestType) { + std::vector b{{5}, {3}, {4}, {1}, {2}}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +struct NegativeTestType { + int value; + auto operator<=>(const NegativeTestType& other) const = default; +}; + +TEST(Sort, CustomNegativeTestType) { + std::vector b{{-5}, {-3}, {-4}, {-1}, {-2}}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +struct FloatTestType { + float value; + auto operator<=>(const FloatTestType& other) const = default; +}; + +TEST(Sort, CustomFloatTestType) { + std::vector b{{5.3f}, {3.1f}, {4.7f}, {1.2f}, {2.8f}}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +struct PairTestType { + int first; + double second; + auto operator<=>(const PairTestType& other) const = default; +}; + +TEST(Sort, CustomPairTestType) { + std::vector b{ + {5, 3.14}, {3, 2.71}, {4, 1.62}, {1, 0.57}, {2, 6.28}}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +struct BoolTestType { + bool value; + auto operator<=>(const BoolTestType& other) const = default; +}; + +TEST(Sort, CustomBoolTestType) { + std::vector b{{true}, {false}, {true}, {false}, {true}}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +struct CharTestType { + char value; + auto operator<=>(const CharTestType& other) const = default; +}; + +TEST(Sort, CustomCharTestType) { + std::vector b{{'e'}, {'a'}, {'c'}, {'b'}, {'d'}}; + MergeSort(b); + EXPECT_TRUE(std::is_sorted(b.begin(), b.end())); +} + +TEST(Sort, VeryLargeNumbers_MergeSort) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis( + std::numeric_limits::min(), std::numeric_limits::max()); + + std::vector a; + + for (int i = 0; i < 1000000; ++i) { + int64_t num = dis(gen); + a.push_back(num); + } + + MergeSort(a); + EXPECT_TRUE(std::is_sorted(a.begin(), a.end())); +} + +TEST(Sort, VeryLongStrings_MergeSort) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis('a', 'z'); + + std::vector a; + + for (int i = 0; i < 10000; ++i) { + std::string str(1000, 'a'); + for (char& c : str) { + c = dis(gen); + } + a.push_back(str); + } + + MergeSort(a); + EXPECT_TRUE(std::is_sorted(a.begin(), a.end())); } diff --git a/task_06/src/nth_element.h b/task_06/src/nth_element.h new file mode 100644 index 00000000..5d6534a9 --- /dev/null +++ b/task_06/src/nth_element.h @@ -0,0 +1,67 @@ +#include + +template +std::size_t Division(std::vector data, std::size_t left, std::size_t right) { + std::size_t half = (left + right - 1) / 2; // середина списка + + if (data[left] < data[half]) { + if (data[right] < data[left]) + return left; + else if (data[right] < data[half]) + return right; + return half; + } else { + if (data[right] < data[half]) + return half; + else if (data[right] < data[left]) + return right; + return left; + } +} + +template +std::size_t Partition(std::vector& data, std::size_t left, + std::size_t right) { + std::size_t pivotPos = Division(data, left, right); + + if (pivotPos != right - 1) { // меняем местами опорный элемент с последним + std::swap(data[right - 1], data[pivotPos]); + } + + std::size_t i = left; + std::size_t j = left; + T pivot = data[right - 1]; + while (j < right - 1) { + if (data[j] <= pivot) { + // текущий элемент не больше опорного + // меняем его с первым из больших + std::swap(data[i++], data[j]); + } + ++j; + } + if (i != right - 1) { // ставим опорный элемент на место + std::swap(data[i], data[right - 1]); + } + return i; +} + +// поиск к-ой порядковой статистики +template +T NthElement(std::vector& data, std::size_t k) { + std::size_t lastPivotPos = 0; + std::size_t left = 0; + std::size_t right = data.size(); + + while (left < right) { + if ((lastPivotPos = Partition(data, left, right)) == k) + return data[lastPivotPos]; + else if (lastPivotPos > k) { + // опорный элемент оказался правее искомого + right = lastPivotPos; + } else { + // опорный элемент не дошел до искомого + left = lastPivotPos + 1; + } + } + return data[lastPivotPos]; +} \ No newline at end of file diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617e..6ca4b25b 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,68 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include + +#include "nth_element.h" + +TEST(NthElement, Sorted) { + std::vector v{1, 2, 3, 4, 5, 6}; + ASSERT_EQ(NthElement(v, 0), 1); + ASSERT_EQ(NthElement(v, 1), 2); + ASSERT_EQ(NthElement(v, 2), 3); + ASSERT_EQ(NthElement(v, 3), 4); + ASSERT_EQ(NthElement(v, 4), 5); + ASSERT_EQ(NthElement(v, 5), 6); +} +TEST(NthElement, InverseSorted) { + std::vector v{6, 5, 4, 3, 2, 1}; + ASSERT_EQ(NthElement(v, 0), 1); + ASSERT_EQ(NthElement(v, 1), 2); + ASSERT_EQ(NthElement(v, 2), 3); + ASSERT_EQ(NthElement(v, 3), 4); + ASSERT_EQ(NthElement(v, 4), 5); + ASSERT_EQ(NthElement(v, 5), 6); +} +//[8, 22, 37, 40, 60, 61, 63, 84, 97, 99] + +TEST(NthElement, Integers) { + std::vector v{1, 10, 2, 9, 3, 8, 4, 7, 5, 6}; + ASSERT_EQ(NthElement(v, 0), 1); + ASSERT_EQ(NthElement(v, 1), 2); + ASSERT_EQ(NthElement(v, 2), 3); + ASSERT_EQ(NthElement(v, 3), 4); + ASSERT_EQ(NthElement(v, 4), 5); + ASSERT_EQ(NthElement(v, 5), 6); + ASSERT_EQ(NthElement(v, 6), 7); + ASSERT_EQ(NthElement(v, 7), 8); + ASSERT_EQ(NthElement(v, 8), 9); + ASSERT_EQ(NthElement(v, 9), 10); +} +TEST(NthElement, Doubles) { + std::vector v{37.75, 99.53, 63.24, 22.56, 37.74, + 60.32, 61.48, 8.15, 84.52, 97.93}; + ASSERT_EQ(NthElement(v, 0), 8.15); + ASSERT_EQ(NthElement(v, 1), 22.56); + ASSERT_EQ(NthElement(v, 2), 37.74); + ASSERT_EQ(NthElement(v, 3), 37.75); + ASSERT_EQ(NthElement(v, 4), 60.32); + ASSERT_EQ(NthElement(v, 5), 61.48); + ASSERT_EQ(NthElement(v, 6), 63.24); + ASSERT_EQ(NthElement(v, 7), 84.52); + ASSERT_EQ(NthElement(v, 8), 97.93); + ASSERT_EQ(NthElement(v, 9), 99.53); } + +TEST(NthElement, Characters) { + std::vector v{'d', 's', 'a', 'b'}; + ASSERT_EQ(NthElement(v, 1), 'b'); +} + +TEST(NthElement, Duplicates) { + std::vector v{2, 2, 2, 2, 2, 2}; + ASSERT_EQ(NthElement(v, 0), 2); + ASSERT_EQ(NthElement(v, 1), 2); + ASSERT_EQ(NthElement(v, 2), 2); + ASSERT_EQ(NthElement(v, 3), 2); + ASSERT_EQ(NthElement(v, 4), 2); + ASSERT_EQ(NthElement(v, 5), 2); +} \ No newline at end of file diff --git a/task_07/src/AVLTree.cpp b/task_07/src/AVLTree.cpp new file mode 100644 index 00000000..acaf1b40 --- /dev/null +++ b/task_07/src/AVLTree.cpp @@ -0,0 +1,98 @@ +#include "AVLTree.h" + +#include +#include + +void AVLTree::Insert(int value) { root_ = Insert(root_, value); } +void AVLTree::Remove(int value) { root_ = Remove(root_, value); } +bool AVLTree::Contains(int value) const { return Contains(root_, value); } + +Node* AVLTree::Insert(Node* root, int value) { + if (!root) return new Node(value); + if (value < root->key) + root->left = Insert(root->left, value); + else + root->right = Insert(root->right, value); + return Balance(root); +} + +Node* AVLTree::Remove(Node* root, int value) { + if (!root) return 0; + if (value < root->key) + root->left = Remove(root->left, value); + else if (value > root->key) + root->right = Remove(root->right, value); + else if (value == root->key) { + Node* new_node_1 = root->left; + Node* new_node_2 = root->right; + delete root; + if (!new_node_2) return new_node_1; + Node* min = FindMin(new_node_2); + min->right = RemoveMin(new_node_2); + min->left = new_node_1; + return Balance(min); + } else { + throw std::runtime_error("AVLTree does not contain this element"); + } + return Balance(root); +} + +bool AVLTree::Contains(Node* node, int value) const { + if (!node) return false; + if (value < node->key) return Contains(node->left, value); + if (value > node->key) return Contains(node->right, value); + return true; +} + +Node* AVLTree::FindMin(Node* root) const { + return root->left ? FindMin(root->left) : root; +} + +Node* AVLTree::RemoveMin(Node* root) { + if (!root->left) return root->right; + root->left = RemoveMin(root->left); + return Balance(root); +} + +Node* AVLTree::Balance(Node* node) { + FixHeight(node); + if (Difference(node) == 2) { + if (Difference(node->right) < 0) node->right = RightRotate(node->right); + return LeftRotate(node); + } + if (Difference(node) == -2) { + if (Difference(node->left) > 0) node->left = LeftRotate(node->left); + return RightRotate(node); + } + return node; +} + +Node* AVLTree::RightRotate(Node* node) { + Node* new_node = node->left; + node->left = new_node->right; + new_node->right = node; + FixHeight(node); + FixHeight(new_node); + return new_node; +} + +Node* AVLTree::LeftRotate(Node* node) { + Node* new_node = node->right; + node->right = new_node->left; + new_node->left = node; + FixHeight(node); + FixHeight(new_node); + return new_node; +} + +unsigned AVLTree::Height(Node* node) const { return node ? node->height : 0; } + +int AVLTree::Difference(Node* node) const { + return Height(node->right) - Height(node->left); +} + +void AVLTree::FixHeight(Node* node) { + unsigned left_height = Height(node->left); + unsigned right_height = Height(node->right); + node->height = std::max(left_height, right_height) + 1; +} \ No newline at end of file diff --git a/task_07/src/AVLTree.h b/task_07/src/AVLTree.h new file mode 100644 index 00000000..36567436 --- /dev/null +++ b/task_07/src/AVLTree.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +struct Node { + Node(int k) : key{k} {} + int key; + unsigned height = 1; + Node* left = nullptr; + Node* right = nullptr; +}; + +class AVLTree { + public: + void Insert(int value); + void Remove(int value); + bool Contains(int value) const; + + private: + Node* root_ = nullptr; + + Node* Insert(Node* root, int value); + Node* Remove(Node* root, int value); + bool Contains(Node* node, int value) const; + + Node* FindMin(Node* root) const; + Node* RemoveMin(Node* root); + + Node* Balance(Node* node); + + Node* RightRotate(Node* node); + Node* LeftRotate(Node* node); + + unsigned Height(Node* node) const; + int Difference(Node* node) const; + void FixHeight(Node* node); +}; \ No newline at end of file diff --git a/task_07/src/test.cpp b/task_07/src/test.cpp index 5e11617e..ac943673 100644 --- a/task_07/src/test.cpp +++ b/task_07/src/test.cpp @@ -1,6 +1,86 @@ - +#include #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "AVLTree.h" + +TEST(AVLTreeTest, InsertAndContains) { + AVLTree tree; + + tree.Insert(10); + tree.Insert(5); + tree.Insert(15); + + ASSERT_EQ(tree.Contains(10), true); + tree.Remove(10); + ASSERT_EQ(tree.Contains(5), true); + ASSERT_EQ(tree.Contains(15), true); + ASSERT_EQ(tree.Contains(20), false); + ASSERT_EQ(tree.Contains(10), false); +} + +TEST(AVLTreeTest, DuplicateInsert) { + AVLTree tree; + tree.Insert(5); + tree.Insert(5); + tree.Insert(5); + ASSERT_EQ(tree.Contains(5), true); +} + +TEST(AVLTreeTest, RemoveNonExisting) { + AVLTree tree; + tree.Insert(5); + tree.Remove(10); + ASSERT_EQ(tree.Contains(5), true); + ASSERT_EQ(tree.Contains(10), false); +} + +TEST(AVLTreeTest, LeftRotation) { + AVLTree tree; + tree.Insert(30); + tree.Insert(20); + tree.Insert(10); + ASSERT_EQ(tree.Contains(10), true); + ASSERT_EQ(tree.Contains(20), true); + ASSERT_EQ(tree.Contains(30), true); } + +TEST(AVLTreeTest, RightRotation) { + AVLTree tree; + tree.Insert(10); + tree.Insert(20); + tree.Insert(30); + ASSERT_EQ(tree.Contains(10), true); + ASSERT_EQ(tree.Contains(20), true); + ASSERT_EQ(tree.Contains(30), true); +} + +TEST(AVLTreeTest, RandomData) { + AVLTree tree; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dist(1, 1000); + + std::vector data(100); + for (int i = 0; i < 100; ++i) { + data[i] = dist(gen); + } + + std::shuffle(data.begin(), data.end(), gen); + + for (int value : data) { + tree.Insert(value); + } + + for (int value : data) { + ASSERT_EQ(tree.Contains(value), true); + } + + std::shuffle(data.begin(), data.end(), gen); + for (int i = 0; i < 50; ++i) { + tree.Remove(data[i]); + } + + for (int i = 50; i < 100; ++i) { + ASSERT_EQ(tree.Contains(data[i]), true); + } +} \ No newline at end of file diff --git a/task_08/src/hashtable.cpp b/task_08/src/hashtable.cpp new file mode 100644 index 00000000..c0df7545 --- /dev/null +++ b/task_08/src/hashtable.cpp @@ -0,0 +1,83 @@ +#include "hashtable.h" + +#include + +#include +#include + +std::size_t HashTable::FirstHashFunc(std::size_t key) { + return floor(buf_size * + ((double(key) * hash_coef) - floor(double(key) * hash_coef))); +} + +std::size_t HashTable::SecondHashFunc(std::size_t key) { + return (key * buf_size - 1) % buf_size; +} + +void HashTable::Clear() { + cells = 0; + size = 0; + for (auto &cell : cell_states) cell = State::Vacant; +} + +void HashTable::Resize() { + buf_size *= 2; + data.resize(buf_size); + cell_states.resize(buf_size); +} + +void HashTable::Rehash() { + std::vector subdata; + for (std::size_t i{0}; i < buf_size; i++) + if (cell_states[i] == State::Occupied) subdata.push_back(data[i]); + + Resize(); + Clear(); + for (auto &elem : subdata) Insert(elem); +} + +bool HashTable::Contains(std::size_t value) { + std::size_t hash = FirstHashFunc(value) % buf_size; + std::size_t count = 0; + while (cell_states[hash] != State::Vacant) { + if (data[hash] == value && cell_states[hash] == State::Occupied) + return true; + count++; + hash = (FirstHashFunc(value) + count * SecondHashFunc(value)) % buf_size; + } + return false; +} + +void HashTable::Insert(std::size_t value) { + std::size_t hash = FirstHashFunc(value) % buf_size; + std::size_t count = 0; + while (cell_states[hash] == State::Occupied) { + if (data[hash] == value) return; + count++; + hash = (FirstHashFunc(value) + count * SecondHashFunc(value)) % buf_size; + } + + data[hash] = value; + cell_states[hash] = State::Occupied; + cells++; + size++; + + double cells_coefficient = double(cells) / double(buf_size); + if (cells_coefficient >= rehash_coef) Rehash(); +} + +void HashTable::Remove(std::size_t value) { + std::size_t hash = FirstHashFunc(value) % buf_size; + std::size_t count = 0; + while (cell_states[hash] != State::Vacant) { + if (data[hash] == value && cell_states[hash] == State::Occupied) { + cell_states[hash] = State::Deleted; + size--; + break; + } + if (data[hash] == value && cell_states[hash] == State::Deleted) break; + + count++; + hash = (FirstHashFunc(value) + SecondHashFunc(value) * count) % buf_size; + } +} \ No newline at end of file diff --git a/task_08/src/hashtable.h b/task_08/src/hashtable.h new file mode 100644 index 00000000..a9e1c240 --- /dev/null +++ b/task_08/src/hashtable.h @@ -0,0 +1,50 @@ +#pragma once +#include + +class HashTable { + public: + HashTable() : size{0}, cells{0}, buf_size{8} { + data = std::vector(buf_size); + cell_states = std::vector(buf_size, State::Vacant); + } + + /// @brief Проверяет наличие значение в хэш-таблице + /// @param value: Значение + /// @return Результат проверки + bool Contains(std::size_t value); + + /// @brief Добавляет значение в хэш-таблицу + /// @param value: Значение + void Insert(std::size_t value); + + /// @brief Удаляет значение из хэш-таблицы + /// @param value: Значение + void Remove(std::size_t value); + + /// @brief Очищает хэш-таблицу + void Clear(); + + std::size_t Size() { return size; } + + private: + constexpr static const double hash_coef = 0.618033989; + constexpr static const double rehash_coef = 0.7; + + enum class State { Occupied, Deleted, Vacant }; + + std::size_t buf_size; + std::size_t size; + std::size_t cells; + + std::vector data; + std::vector cell_states; + + std::size_t FirstHashFunc(std::size_t key); + std::size_t SecondHashFunc(std::size_t key); + + /// @brief Меняет размер + void Resize(); + + /// @brief Рехэш + void Rehash(); +}; \ No newline at end of file diff --git a/task_08/src/test.cpp b/task_08/src/test.cpp index 5e11617e..73f97ae3 100644 --- a/task_08/src/test.cpp +++ b/task_08/src/test.cpp @@ -1,6 +1,41 @@ - #include +#include + +#include + +TEST(HashTableTest, Simple) { + HashTable table; + table.Insert(5); + ASSERT_TRUE(table.Contains(5)); + + table.Insert(14); + ASSERT_TRUE(table.Contains(14)); + + table.Insert(45); + table.Remove(45); + ASSERT_FALSE(table.Contains(45)); + + table.Insert(41420); + table.Clear(); + ASSERT_EQ(table.Size(), 0); + + table.Insert(245); + table.Insert(301); + ASSERT_EQ(table.Size(), 2); + + for (int i = 0; i < 1000; ++i) { + table.Insert(i); + } + table.Clear(); + ASSERT_EQ(table.Size(), 0); + + table.Insert(5); + table.Insert(15); + ASSERT_TRUE(table.Contains(5)); + ASSERT_TRUE(table.Contains(15)); -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] -} + for (int i = 0; i < 100; i++) table.Insert(i); + for (int i = 0; i <= 50; i++) table.Remove(100 - i); + for (int i = 0; i < 38; i++) table.Insert(i); + ASSERT_EQ(table.Size(), 50); +} \ No newline at end of file diff --git a/task_09/src/min_amount_of_coins.cpp b/task_09/src/min_amount_of_coins.cpp new file mode 100644 index 00000000..246ecd69 --- /dev/null +++ b/task_09/src/min_amount_of_coins.cpp @@ -0,0 +1,11 @@ +#include "min_amount_of_coins.h" + +size_t MinAmountOfCoins(size_t sum, std::vector coins) { + if (coins.empty()) throw std::runtime_error("there is no coins"); + std::vector table(sum + 1, std::numeric_limits::max()); + table[0] = 0; + for (size_t i = 1; i <= sum; ++i) + for (const size_t j : coins) + if (j <= i) table[i] = std::min(table[i], table[i - j] + 1); + return table[sum]; +} \ No newline at end of file diff --git a/task_09/src/min_amount_of_coins.h b/task_09/src/min_amount_of_coins.h new file mode 100644 index 00000000..12120269 --- /dev/null +++ b/task_09/src/min_amount_of_coins.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include + +/// @brief Определяет наименьшее количество монет из coins, чтобы набрать sum +size_t MinAmountOfCoins(size_t sum, std::vector coins); \ No newline at end of file diff --git a/task_09/src/test.cpp b/task_09/src/test.cpp index 869094dd..63948e9b 100644 --- a/task_09/src/test.cpp +++ b/task_09/src/test.cpp @@ -1,4 +1,16 @@ - #include -TEST(TopologySort, Simple) { ASSERT_EQ(1, 1); } +#include "min_amount_of_coins.h" + +TEST(MinAmountOfCoins, Simple) { + std::vector coins_1{1, 2, 5, 10}; + std::vector coins_2{1, 7, 10}; + ASSERT_EQ(MinAmountOfCoins(14, coins_1), 3); + ASSERT_EQ(MinAmountOfCoins(19, coins_1), 4); + ASSERT_EQ(MinAmountOfCoins(15, coins_2), 3); + ASSERT_EQ(MinAmountOfCoins(14, coins_2), 2); + ASSERT_EQ(MinAmountOfCoins(21, coins_2), 3); + ASSERT_EQ(MinAmountOfCoins(34, coins_2), 4); + ASSERT_THROW(MinAmountOfCoins(14, std::vector{}), std::runtime_error); + ASSERT_EQ(MinAmountOfCoins(0, coins_1), 0); +} \ No newline at end of file