diff --git a/.github/workflows/clang-tidy-review.yaml b/.github/workflows/clang-tidy-review.yaml index 50c0ad37..b2bde92d 100644 --- a/.github/workflows/clang-tidy-review.yaml +++ b/.github/workflows/clang-tidy-review.yaml @@ -14,7 +14,7 @@ jobs: - uses: ZedThree/clang-tidy-review@v0.10.1 with: - clang_tidy_checks: '-*,performance-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-avoid-magic-numbers' + clang_tidy_checks: '-*,performance-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-avoid-magic-numbers,-clang-diagnostic-error,-misc-no-recursion' split_workflow: true - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/cmake.yaml b/.github/workflows/tests.yaml similarity index 100% rename from .github/workflows/cmake.yaml rename to .github/workflows/tests.yaml diff --git a/additional_tasks/CMakeLists.txt b/additional_tasks/CMakeLists.txt index e298bd2e..ad72f75c 100644 --- a/additional_tasks/CMakeLists.txt +++ b/additional_tasks/CMakeLists.txt @@ -2,4 +2,17 @@ cmake_minimum_required(VERSION 3.10) project(additional_tasks) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/temperature) +file(GLOB_RECURSE tasks_dirs LIST_DIRECTORIES true ".") + +MESSAGE("dirs: ${task_dirs}") + +foreach(dir ${tasks_dirs}) + IF(IS_DIRECTORY ${dir}) + IF(NOT ${dir} MATCHES ".*src.*") + MESSAGE("additional task: ${dir}") + add_subdirectory(${dir}) + ENDIF() + ELSE() + CONTINUE() + ENDIF() +endforeach() diff --git a/additional_tasks/find_sum_numbers/CMakeLists.txt b/additional_tasks/find_sum_numbers/CMakeLists.txt index 7eac3977..28c5cb5a 100644 --- a/additional_tasks/find_sum_numbers/CMakeLists.txt +++ b/additional_tasks/find_sum_numbers/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME find_number_sum) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/additional_tasks/find_sum_numbers/src/test.cpp b/additional_tasks/find_sum_numbers/src/test.cpp index a017e409..fe734e9e 100644 --- a/additional_tasks/find_sum_numbers/src/test.cpp +++ b/additional_tasks/find_sum_numbers/src/test.cpp @@ -5,4 +5,12 @@ #include "utils.hpp" -TEST(GetNumbersOfSum, Simple) {} \ No newline at end of file +TEST(GetNumbersOfSum, Simple) { + ASSERT_EQ(GetNumbersOfSum(std::vector{-10, -2, 0, 3, 6, 7, 11, 14}, 3), + (std::pair(0, 3))); + ASSERT_ANY_THROW(GetNumbersOfSum(std::vector{}, 3)); + ASSERT_EQ(GetNumbersOfSum(std::vector{0}, 3), + (std::pair(0, 0))); + ASSERT_EQ(GetNumbersOfSum(std::vector{1, 2, 4, 4, 7}, 9), + (std::pair(2, 7))); +} \ No newline at end of file diff --git a/additional_tasks/find_sum_numbers/src/utils.cpp b/additional_tasks/find_sum_numbers/src/utils.cpp index 7ad97cdc..e1ad1e81 100644 --- a/additional_tasks/find_sum_numbers/src/utils.cpp +++ b/additional_tasks/find_sum_numbers/src/utils.cpp @@ -3,5 +3,14 @@ #include std::pair GetNumbersOfSum(const std::vector& numbers, int sum) { - return {0, 0}; + if (numbers.empty()) throw std::out_of_range("Empty list of numbers"); + int i = 0; + int j = numbers.size() - 1; + while (i < j) { + if (numbers.at(i) + numbers.at(j) == sum) + return std::pair(numbers.at(i), numbers.at(j)); + if (numbers.at(i) + numbers.at(j) < sum) ++i; + if (numbers.at(i) + numbers.at(j) > sum) --j; + } + return std::pair(0, 0); } \ No newline at end of file diff --git a/additional_tasks/find_sum_numbers/src/utils.hpp b/additional_tasks/find_sum_numbers/src/utils.hpp index 6b4ca38d..ea688a1c 100644 --- a/additional_tasks/find_sum_numbers/src/utils.hpp +++ b/additional_tasks/find_sum_numbers/src/utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include std::pair GetNumbersOfSum(const std::vector& numbers, int sum); diff --git a/additional_tasks/temperature/CMakeLists.txt b/additional_tasks/temperature/CMakeLists.txt index 70332489..28c5cb5a 100644 --- a/additional_tasks/temperature/CMakeLists.txt +++ b/additional_tasks/temperature/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME days_before_warmup) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/additional_tasks/temperature/src/utils.cpp b/additional_tasks/temperature/src/utils.cpp index e2f83df3..4f149dd8 100644 --- a/additional_tasks/temperature/src/utils.cpp +++ b/additional_tasks/temperature/src/utils.cpp @@ -1,8 +1,46 @@ #include "utils.hpp" #include +#include +#include + +struct Day { + float temperature; + int index; + Day(float temperature, int index) : temperature{temperature}, index{index} {} +}; std::vector CalculateDaysBeforWarmup( - const std::vector& temerature) { - return {}; + const std::vector& temperature) { + const size_t size = temperature.size(); + std::stack add_stack; + std::vector res(size); + + for (int i = size - 1; i > -1; --i) { + if (add_stack.empty()) { + add_stack.push(Day(temperature.at(i), i)); + res[i] = 0; + continue; + } + if (temperature[i] < add_stack.top().temperature) { + res[i] = add_stack.top().index - i; + add_stack.push(Day(temperature.at(i), i)); + continue; + } + if (temperature[i] >= add_stack.top().temperature) { + while (!add_stack.empty() && + temperature[i] > add_stack.top().temperature) { + add_stack.pop(); + } + if (add_stack.empty()) { + add_stack.push(Day(temperature.at(i), i)); + res[i] = 0; + continue; + } + res[i] = add_stack.top().index - i; + add_stack.push(Day(temperature.at(i), i)); + continue; + } + } + return res; } diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 0105689d..28c5cb5a 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME sandbox) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -24,11 +25,11 @@ find_package(GTest REQUIRED) include_directories(${GTEST_INCLUDE_DIRS}) # Link runTests with what we want to test and the GTest and pthread library -add_executable(sandbox_tests ${test_source_list}) +add_executable(${PROJECT_NAME}_tests ${test_source_list}) target_link_libraries( - sandbox_tests + ${PROJECT_NAME}_tests GTest::gtest_main ) include(GoogleTest) -gtest_discover_tests(sandbox_tests) +gtest_discover_tests(${PROJECT_NAME}_tests) diff --git a/sandbox/src/main.cpp b/sandbox/src/main.cpp index ce52ad7a..a2affcf9 100644 --- a/sandbox/src/main.cpp +++ b/sandbox/src/main.cpp @@ -1,4 +1,22 @@ +#include + #include #include -int main() { return 0; } +int divCount(int n) { + for (int j = 2; j <= n / 5 + 1; ++j) { + if (n % static_cast(pow(5, j)) != 0) return j - 1; + } + return n / 5; +} + +int main() { + int n; + std::cin >> n; + int res = 0; + for (int i = 5; i <= n; i += 5) { + res += divCount(i); + } + std::cout << res; + return 0; +} diff --git a/task_01/CMakeLists.txt b/task_01/CMakeLists.txt index 0d13e1cf..28c5cb5a 100644 --- a/task_01/CMakeLists.txt +++ b/task_01/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME task_01) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/task_01/src/stack.cpp b/task_01/src/stack.cpp index 8ca89902..ac78eeaa 100644 --- a/task_01/src/stack.cpp +++ b/task_01/src/stack.cpp @@ -1,21 +1,38 @@ #include "stack.hpp" #include +#include -void Stack::Push(int value) { data_.push(value); } +void Stack::Push(int value) { data_.push_back(value); } int Stack::Pop() { - auto result = data_.top(); - data_.pop(); + if (data_.empty()) throw std::out_of_range{"Empty stack"}; + auto result = data_.back(); + data_.pop_back(); return result; } -void MinStack::Push(int value) { data_.push_back(value); } +bool Stack::IsEmpty() { return data_.empty(); } + +void MinStack::Push(int value) { + if (minimums_.IsEmpty()) { + minimums_.Push(value); + } else if (value <= data_.back()) { + minimums_.Push(value); + } + data_.push_back(value); +} int MinStack::Pop() { + if (data_.empty()) throw std::out_of_range{"Empty stack"}; auto result = data_.back(); data_.pop_back(); + if (result == minimums_.GetLast()) { + minimums_.Pop(); + } return result; } -int MinStack::GetMin() { return *std::min_element(data_.begin(), data_.end()); } \ No newline at end of file +int Stack::GetLast() { return data_.back(); } + +int MinStack::GetMin() { return minimums_.GetLast(); } \ No newline at end of file diff --git a/task_01/src/stack.hpp b/task_01/src/stack.hpp index b784b235..985cf767 100644 --- a/task_01/src/stack.hpp +++ b/task_01/src/stack.hpp @@ -1,23 +1,25 @@ #pragma once -#include #include class Stack { public: - void Push(int value); - int Pop(); + virtual void Push(int value); + virtual int Pop(); + bool IsEmpty(); - private: - std::stack data_; + int GetLast(); + + protected: + std::vector data_; }; -class MinStack { +class MinStack : Stack { public: - void Push(int value); - int Pop(); + void Push(int value) override; + int Pop() override; int GetMin(); private: - std::vector data_; + Stack minimums_; }; \ No newline at end of file diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 54e7ce90..102fa0e2 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -39,4 +39,17 @@ TEST(MinStackTest, Simple) { ASSERT_EQ(stack.GetMin(), 1); ASSERT_EQ(stack.Pop(), 3); // Stack [1] ASSERT_EQ(stack.Pop(), 1); // Stack [] +} + +TEST(StackTest, Additional) { + Stack stack; + stack.Push(9); // Stack [9] + stack.Push(-3); // Stack [9, -3] + stack.Push(2); // Stack [9, 3, 2] + ASSERT_EQ(stack.GetLast(), 2); // Stack [9, 3, 2] + stack.Push(5); + stack.Push(4); + stack.Push(4); + stack.Pop(); + ASSERT_EQ(stack.GetLast(), 4); } \ No newline at end of file diff --git a/task_02/CMakeLists.txt b/task_02/CMakeLists.txt index 08b6e047..28c5cb5a 100644 --- a/task_02/CMakeLists.txt +++ b/task_02/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME task_02) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/task_02/src/heap.cpp b/task_02/src/heap.cpp deleted file mode 100644 index d80490ec..00000000 --- a/task_02/src/heap.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "heap.hpp" - -#include -#include - -void MinHeap::Push(int n) { data_.push(n); } - -int MinHeap::Pop() { - if (data_.empty()) throw std::out_of_range("Empty heap"); - auto result = data_.top(); - data_.pop(); - return result; -} - -size_t MinHeap::Size() { return data_.size(); } diff --git a/task_02/src/heap.hpp b/task_02/src/heap.hpp index 65b67d2e..e6186786 100644 --- a/task_02/src/heap.hpp +++ b/task_02/src/heap.hpp @@ -1,16 +1,90 @@ #pragma once +#include +#include #include #include -#include +#include #include +template class MinHeap { public: - void Push(int value); - int Pop(); + void Push(T value); + T PopMin(); + T GetMin(); size_t Size(); private: - std::priority_queue, std::greater> data_; + std::vector data_; + void SiftUp(int currindex); + void SiftDown(); + void ChangeHigh(); + size_t height_ = 0; }; + +template +void MinHeap::SiftDown() { + if (data_.size() <= 1) return; + int curr_index = 0; // start on the top + for (int i = 0, j = 1; i < height_ - 1; ++i, j = 2 * i + 1) { + if (data_[j] < data_[j + 1] && data_[j] < data_[i]) { + std::swap(data_[curr_index], data_[j]); + curr_index = j; + } else if (data_[j] > data_[j + 1] && data_[j + 1] < data_[i]) { + std::swap(data_[curr_index], data_[j + 1]); + curr_index = j + 1; + } else + break; // as low as possible + } +} + +template +void MinHeap::SiftUp(int curr_index) { + if (data_.size() <= 1) return; + for (int i = height_; i > 0; ++i) { + if (data_[curr_index] < data_[(curr_index - 1) / 2]) { + std::swap(data_[curr_index], data_[(curr_index - 1) / 2]); + curr_index = (curr_index - 1) / 2; + } else + break; + } +} + +template +void MinHeap::Push(T n) { + data_.push_back(n); + ChangeHigh(); + SiftUp(data_.size() - 1); +} + +template +T MinHeap::PopMin() { + if (data_.empty()) throw std::out_of_range("Empty heap"); + auto result = data_[0]; + data_[0] = data_.back(); + data_.pop_back(); + ChangeHigh(); + SiftDown(); + return result; +} + +template +size_t MinHeap::Size() { + return data_.size(); +} + +template +void MinHeap::ChangeHigh() { + if (data_.size() <= 2) { + height_ = data_.size(); + return; + } + height_ = static_cast(floor(log2(data_.size() - 1)) + 1); +} + +template +T MinHeap::GetMin() { + if (data_.empty()) throw std::out_of_range("Empty heap"); + return data_[0]; +} \ No newline at end of file diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 9bd764d1..57f93455 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -4,53 +4,76 @@ #include "heap.hpp" TEST(HeapTest, Simple) { - MinHeap heap; + MinHeap heap; heap.Push(1); - ASSERT_EQ(heap.Pop(), 1); + ASSERT_EQ(heap.PopMin(), 1); } TEST(HeapTest, Basic) { - MinHeap heap; + MinHeap heap; heap.Push(1); heap.Push(2); - heap.Push(3); // [1, 2, 3] - ASSERT_EQ(heap.Pop(), 1); // [2, 3] -> 1 - ASSERT_EQ(heap.Pop(), 2); // [3] -> 2 - ASSERT_EQ(heap.Pop(), 3); // [] -> 3 + heap.Push(3); // [1, 2, 3] + ASSERT_EQ(heap.PopMin(), 1); // [2, 3] -> 1 + ASSERT_EQ(heap.PopMin(), 2); // [3] -> 2 + ASSERT_EQ(heap.PopMin(), 3); // [] -> 3 } TEST(HeapTest, Empty) { - MinHeap heap; - ASSERT_ANY_THROW(heap.Pop()); + MinHeap heap; + ASSERT_ANY_THROW(heap.PopMin()); heap.Push(1); - ASSERT_EQ(heap.Pop(), 1); // [(1), 2, 3] - ASSERT_ANY_THROW(heap.Pop()); + ASSERT_EQ(heap.PopMin(), 1); // [(1), 2, 3] + ASSERT_ANY_THROW(heap.PopMin()); + ASSERT_ANY_THROW(heap.GetMin()); } TEST(HeapTest, Complex) { - MinHeap heap; - heap.Push(6); // [6] - heap.Push(1); // [1, 6] - heap.Push(2); // [1, 2, 6] - heap.Push(3); // [1, 2, 3, 6] - heap.Push(5); // [1, 2, 3, 5, 6] - heap.Push(4); // [1, 2, 3, 4, 5, 6] - ASSERT_EQ(heap.Pop(), 1); // [2, 3, 4, 5, 6] -> 1 - ASSERT_EQ(heap.Pop(), 2); // [3, 4, 5, 6] -> 2 - ASSERT_EQ(heap.Pop(), 3); // [4, 5, 6] -> 3 - ASSERT_EQ(heap.Pop(), 4); // [5, 6] -> 4 - ASSERT_EQ(heap.Pop(), 5); // [6] -> 5 - heap.Push(7); // [6, 7] - heap.Push(1); // [1, 6, 7] - heap.Push(2); // [1, 2, 6, 7] - ASSERT_EQ(heap.Pop(), 1); // [2, 6, 7] -> 1 - heap.Push(3); // [2, 3, 6, 7] - heap.Push(5); // [2, 3, 5, 6, 7] - heap.Push(4); // [2, 3, 4, 5, 6, 7] - ASSERT_EQ(heap.Pop(), 2); // [3, 4, 5, 6, 7] -> 2 - ASSERT_EQ(heap.Pop(), 3); // [4, 5, 6, 7] -> 3 - ASSERT_EQ(heap.Pop(), 4); // [5, 6, 7] -> 4 - ASSERT_EQ(heap.Pop(), 5); // [6, 7] -> 5 - ASSERT_EQ(heap.Pop(), 6); // [7] -> 6 - ASSERT_EQ(heap.Pop(), 7); // [] -> 7 + MinHeap heap; + heap.Push(6); // [6] + heap.Push(1); // [1, 6] + heap.Push(2); // [1, 2, 6] + heap.Push(3); // [1, 2, 3, 6] + heap.Push(5); // [1, 2, 3, 5, 6] + heap.Push(4); // [1, 2, 3, 4, 5, 6] + ASSERT_EQ(heap.PopMin(), 1); // [2, 3, 4, 5, 6] -> 1 + ASSERT_EQ(heap.PopMin(), 2); // [3, 4, 5, 6] -> 2 + ASSERT_EQ(heap.PopMin(), 3); // [4, 5, 6] -> 3 + ASSERT_EQ(heap.PopMin(), 4); // [5, 6] -> 4 + ASSERT_EQ(heap.PopMin(), 5); // [6] -> 5 + heap.Push(7); // [6, 7] + heap.Push(1); // [1, 6, 7] + heap.Push(2); // [1, 2, 6, 7] + ASSERT_EQ(heap.PopMin(), 1); // [2, 6, 7] -> 1 + heap.Push(3); // [2, 3, 6, 7] + heap.Push(5); // [2, 3, 5, 6, 7] + heap.Push(4); // [2, 3, 4, 5, 6, 7] + ASSERT_EQ(heap.PopMin(), 2); // [3, 4, 5, 6, 7] -> 2 + ASSERT_EQ(heap.PopMin(), 3); // [4, 5, 6, 7] -> 3 + ASSERT_EQ(heap.PopMin(), 4); // [5, 6, 7] -> 4 + ASSERT_EQ(heap.PopMin(), 5); // [6, 7] -> 5 + ASSERT_EQ(heap.PopMin(), 6); // [7] -> 6 + ASSERT_EQ(heap.PopMin(), 7); // [] -> 7 +} + +TEST(HeapTest, Additional) { + MinHeap heap; + heap.Push(0); // [0] + heap.Push(-1); // [-1, 0] + heap.Push(-10); // [-10, -1, 0] + ASSERT_EQ(heap.Size(), 3); + ASSERT_EQ(heap.PopMin(), -10); // [-1, 0] +} + +TEST(HeapTest, Double) { + MinHeap heap; + heap.Push(0.1); // [0.1] + heap.Push(-1.9); // [-1.9, 0.1] + heap.Push(-10.8); // [-10.8, -1.9, 0.1] + ASSERT_EQ(heap.Size(), 3); + ASSERT_EQ(heap.PopMin(), -10.8); // [-1.9, 0.1] + ASSERT_EQ(heap.PopMin(), -1.9); // [0.1] + ASSERT_EQ(heap.GetMin(), 0.1); // [0.1] + ASSERT_EQ(heap.PopMin(), 0.1); // [] + ASSERT_ANY_THROW(heap.PopMin()); } \ No newline at end of file diff --git a/task_03/src/sort.cpp b/task_03/src/sort.cpp index 9380a0f4..20a221ba 100644 --- a/task_03/src/sort.cpp +++ b/task_03/src/sort.cpp @@ -1,10 +1,34 @@ #include "sort.hpp" -#include #include -std::vector Sort(const std::vector& data) { - std::vector result = data; - std::sort(result.begin(), result.end()); - return result; +std::vector Sort(const std::vector& input) { + if (input.size() <= 1) return input; + std::vector output; + size_t middle = input.size() / 2 + input.size() % 2; + std::vector smaller_part(input.begin(), input.begin() + middle); + std::vector bigger_part(input.begin() + middle, input.end()); + + bigger_part = Sort(bigger_part); + smaller_part = Sort(smaller_part); + for (size_t i = 0, j = 0; + i < smaller_part.size() || j < bigger_part.size();) { + if (smaller_part[i] <= bigger_part[j]) { + output.push_back(smaller_part[i]); + ++i; + if (i == smaller_part.size()) { + output.insert(output.end(), bigger_part.begin() + j, bigger_part.end()); + break; + } + } else { + output.push_back(bigger_part[j]); + ++j; + if (j == bigger_part.size()) { + output.insert(output.end(), smaller_part.begin() + i, + smaller_part.end()); + break; + } + } + } + return output; } \ No newline at end of file diff --git a/task_04/src/order_statistics.cpp b/task_04/src/order_statistics.cpp index 94e0defd..c7866e51 100644 --- a/task_04/src/order_statistics.cpp +++ b/task_04/src/order_statistics.cpp @@ -1,11 +1,53 @@ #include "order_statistics.hpp" -#include +#include +#include -int GetOrderStatistics(const std::vector& data, size_t n) { - std::vector tmp(data); +size_t GenRandIndex(const size_t n) { + std::random_device rd; // non-deterministic generator + std::mt19937 gen(rd()); // to seed mersenne twister. + std::uniform_int_distribution<> dist(0, n - 1); + return dist(gen); +} + +int QuickSelect(const std::vector& input, const size_t k) { + if (input.size() < k) throw std::out_of_range("Too big statistic"); + std::vector data{input}; + if (data.size() == 1) return data[0]; + size_t pivot = GenRandIndex(data.size()); + + size_t start = 0; + size_t end = data.size(); + std::swap(data[pivot], data[end - 1]); + pivot = end - 1; + size_t i = start, j = start; - std::sort(tmp.begin(), tmp.end()); + while (true) { + for (; j + 1 < end;) { + if (data[j] <= data[pivot]) { + std::swap(data[i], data[j]); + ++i; + ++j; + } + if (data[j] > data[pivot]) ++j; + } + std::swap(data[i], data[pivot]); + if (i == k) return data[i]; + if (i > k) + end = i; + else if (i < k) + start = i; + + pivot = start + GenRandIndex(end - start); + std::swap(data[pivot], data[end - 1]); + pivot = end - 1; + i = start; + j = start; + } +} + +int GetOrderStatistics(const std::vector& data, size_t n) { + int temp = QuickSelect(data, n); - return tmp[n]; + return temp; } diff --git a/task_04/src/order_statistics.hpp b/task_04/src/order_statistics.hpp index 05332cdd..d4294670 100644 --- a/task_04/src/order_statistics.hpp +++ b/task_04/src/order_statistics.hpp @@ -2,4 +2,4 @@ #include -int GetOrderStatistics(const std::vector& data, size_t n); +int GetOrderStatistics(const std::vector& data, const size_t n); diff --git a/task_05/CMakeLists.txt b/task_05/CMakeLists.txt index b217db8f..28c5cb5a 100644 --- a/task_05/CMakeLists.txt +++ b/task_05/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME task_05) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 8ca70db8..e6b8bbe2 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -5,4 +5,8 @@ #include "tree.hpp" -TEST(Tree, Simple) { Tree tree; } \ No newline at end of file +TEST(Tree, Simple) { + Tree tree; + ASSERT_EQ(tree.Insert(1, 2), true); // 1: 2 + ASSERT_EQ(tree.Insert(1, 2), false); // 1: 2 +} \ No newline at end of file diff --git a/task_05/src/tree.cpp b/task_05/src/tree.cpp index 052bb00c..9f2aa7a8 100644 --- a/task_05/src/tree.cpp +++ b/task_05/src/tree.cpp @@ -1,9 +1,204 @@ #include "tree.hpp" +#include "exception" + +bool operator==(const Tree::Node &left, const Tree::Node &right) { + if (left.parent.lock() != right.parent.lock()) return false; + if (left.left_child != right.left_child) return false; + if (left.right_child != right.right_child) return false; + return true; +} + +bool operator!=(const Tree::Node &left, const Tree::Node &right) { + return !(left == right); +} + +Tree::Node::Node(const Node &node) + : parent{node.parent}, + left_child{node.left_child}, + right_child{node.right_child}, + key{node.key}, + value{node.value} {} + Tree::Tree() {} -bool Tree::Insert(int key, int value) { return true; } +Tree::Node::Node(int k, int v) : key{k}, value{v} {} + +bool Tree::Insert(int key, int value) { + auto result = InsertWithoutSplay(key, value); + Splay(result.first); + return result.second; +} + +std::pair Tree::InsertWithoutSplay(int key, int value) { + if (root == nullptr) { + root = std::make_shared(Node(key, value)); + return std::pair(*(root.get()), true); + } + if (root->key < key) + return (Tree(root->left_child).InsertWithoutSplay(key, value)); + if (root->key > key) + return Tree(root->right_child).InsertWithoutSplay(key, value); + if (root->key == key) + return std::pair(*(root.get()), false); +} + +void Tree::InsertOrUpdate(int key, int value) { + auto result = InsertOrUpdateWithoutSplay(key, value); + Splay(result); +} + +Tree::Node &Tree::InsertOrUpdateWithoutSplay(int key, int value) { + if (root == nullptr) { + root = std::shared_ptr(std::make_shared(Node(key, value))); + return *(root.get()); + } + if (root->key < key) + return Tree(root->left_child).InsertOrUpdateWithoutSplay(key, value); + if (root->key > key) + return Tree(root->right_child).InsertOrUpdateWithoutSplay(key, value); + if (root->key == key) { + root->value = value; + return *(root.get()); + } +} + +int Tree::Find(int key) { + auto result = FindWithoutSplay(key); + Splay(result.first); + return result.second; +} + +std::pair Tree::FindWithoutSplay(int key) { + if (root == nullptr) throw std::out_of_range("No such key\n"); + if (root->key < key) Tree(root->left_child).Find(key); + if (root->key > key) Tree(root->right_child).Find(key); + if (root->key == key) + return std::pair(*(root.get()), root->value); +} + +void Tree::Delete(int key) { + auto deleting_node = FindWithoutSplay(key).first; + + if (deleting_node.left_child == nullptr && + deleting_node.right_child == nullptr) { + if (*(root.get()) != deleting_node) { + if (IsLeftChild(deleting_node)) + deleting_node.parent.lock()->left_child = nullptr; + else + deleting_node.parent.lock()->right_child = nullptr; + } + } else + root = nullptr; + + if (deleting_node.left_child == nullptr && + deleting_node.right_child != nullptr) { + if (*(root.get()) != deleting_node) { + if (IsLeftChild(deleting_node)) + deleting_node.parent.lock()->left_child = deleting_node.right_child; + else + deleting_node.parent.lock()->right_child = deleting_node.right_child; + deleting_node.right_child = deleting_node.parent.lock(); + } + } + + if (deleting_node.left_child != nullptr && + deleting_node.right_child == nullptr) { + if (*(root.get()) != deleting_node) { + if (IsLeftChild(deleting_node)) + deleting_node.parent.lock()->left_child = deleting_node.left_child; + else + deleting_node.parent.lock()->right_child = deleting_node.left_child; + deleting_node.left_child = deleting_node.parent.lock(); + } + } + + if (deleting_node.left_child != nullptr && + deleting_node.right_child != nullptr) { + auto new_node = deleting_node.right_child; + while (new_node->left_child != nullptr) new_node = new_node->left_child; + if (IsLeftChild(*(new_node.get()))) + new_node->parent.lock()->left_child = nullptr; + else + new_node->parent.lock()->right_child = nullptr; + new_node->parent = deleting_node.parent; + new_node->left_child = deleting_node.left_child; + new_node->right_child = deleting_node.right_child; + if (IsLeftChild(*(new_node.get()))) + deleting_node.parent.lock()->left_child = new_node; + else + deleting_node.parent.lock()->right_child = new_node; + deleting_node.left_child->parent = new_node; + deleting_node.right_child->parent = new_node; + } +} + +void Tree::Zig(Node &element) { + if (!element.parent.lock()) return; + if (element == *(element.parent.lock()->left_child.get())) { + auto old_parent = element.parent; + auto old_left_child = element.left_child; + auto old_right_child = element.right_child; + *(element.parent.lock()->parent.lock()) = element; + element.right_child = element.parent.lock(); + element.parent = old_parent.lock()->parent; + *(old_parent.lock()->left_child) = element; + old_parent.lock()->left_child = old_right_child; + old_right_child->parent = old_parent; + } + if (element == *(element.parent.lock()->right_child.get())) { + auto old_parent = element.parent; + auto old_right_child = element.right_child; + auto old_left_child = element.left_child; + *(element.parent.lock()->parent.lock()) = element; + element.left_child = element.parent.lock(); + element.parent = old_parent.lock()->parent; + *(old_parent.lock()->right_child) = element; + old_parent.lock()->right_child = old_left_child; + old_left_child->parent = old_parent; + } +} + +void Tree::ZigZig(Node &element) { + Zig(*element.parent.lock()); + Zig(element); +} + +void Tree::ZigZag(Node &element) { + Zig(element); + Zig(element); +} + +void Tree::Splay(Node &element) { + while (element != *(root.get())) { + if (this->HaveGrandpa(element) && IsDirectChild(element)) ZigZig(element); + if (this->HaveGrandpa(element) && !IsDirectChild(element)) ZigZag(element); + if (!(this->HaveGrandpa(element))) Zig(element); + } +} + +bool Tree::IsDirectChild(const Node &element) { + if (IsLeftChild(element) && IsLeftChild(*(element.parent.lock()))) + return true; + if (IsRightChild(element) && IsRightChild(*(element.parent.lock()))) + return true; + return false; +} + +bool Tree::IsLeftChild(const Node &element) { + if (element.parent.lock() == nullptr) return false; + if (element == *(element.parent.lock()->left_child.get())) return true; + return false; +} -void Tree::InsertOrUpdate(int key, int value) {} +bool Tree::IsRightChild(const Node &element) { + if (element.parent.lock() == nullptr) return false; + if (element == *(element.parent.lock()->right_child.get())) return true; + return false; +} -int Tree::Find(int key) const { return 0; } +bool Tree::HaveGrandpa(const Node &element) { + if (element.parent.lock() == nullptr) return false; + if (element.parent.lock()->parent.lock() != nullptr) return true; + return false; +} \ No newline at end of file diff --git a/task_05/src/tree.hpp b/task_05/src/tree.hpp index 584309c0..08fdadbe 100644 --- a/task_05/src/tree.hpp +++ b/task_05/src/tree.hpp @@ -1,9 +1,44 @@ #pragma once +#include class Tree { public: + struct Node { + std::weak_ptr parent; + std::shared_ptr left_child = nullptr; + std::shared_ptr right_child = nullptr; + int value; + int key; + Node(int k, int v); + Node(const Node& node); + + Node& operator=(const Node& rhs) = default; + + Node(Node&& other) = default; + Node& operator=(Node&& rhs) = default; + }; + + private: + void Zig(Node& element); + void ZigZig(Node& element); + void ZigZag(Node& element); + void Splay(Node& element); + std::pair InsertWithoutSplay(int key, int value); + Node& InsertOrUpdateWithoutSplay(int key, int value); + std::pair FindWithoutSplay(int key); + + public: + std::shared_ptr root = nullptr; Tree(); + Tree(std::shared_ptr r) : root{r} {}; + bool Insert(int key, int value); void InsertOrUpdate(int key, int value); - int Find(int key) const; + int Find(int key); + void Delete(int key); + + bool IsLeftChild(const Node& element); + bool IsRightChild(const Node& element); + bool IsDirectChild(const Node& element); + bool HaveGrandpa(const Node& element); }; diff --git a/task_06/CMakeLists.txt b/task_06/CMakeLists.txt index f3f54c2a..28c5cb5a 100644 --- a/task_06/CMakeLists.txt +++ b/task_06/CMakeLists.txt @@ -1,7 +1,8 @@ cmake_minimum_required(VERSION 3.10) -set(PROJECT_NAME task_06) -project(${PROJECT_NAME}) +get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) +string(REPLACE " " "_" PROJECT_NAME ${PROJECT_NAME}) +project(${PROJECT_NAME} C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/task_06/src/hashtable.cpp b/task_06/src/hashtable.cpp index 87d5f1e0..602d19b1 100644 --- a/task_06/src/hashtable.cpp +++ b/task_06/src/hashtable.cpp @@ -1,25 +1,76 @@ #include "hashtable.hpp" #include +#include +#include #include +#include -HashTable::HashTable() {} +HashTable::HashTable(size_t width) : width_(width), data_(width) {} + +bool HashTable::Contains(const std::string& key) { + size_t hash_w = hasher_(key) % width_; + if (data_.at(hash_w).empty()) return false; + + for (size_t i = 0; i < data_.at(hash_w).size(); ++i) + if (data_.at(hash_w).at(i).first == key) return true; + + return false; +} bool HashTable::Insert(const std::string& key, int value) { - if (data_.contains(key)) return false; - data_[key] = value; + size_t hash_w = hasher_(key) % width_; + if (Contains(key)) return false; + data_.at(hash_w).push_back(std::pair(key, value)); + keys_count_ += 1; + this->Rehash(); return true; } void HashTable::InsertOrUpdate(const std::string& key, int value) { - data_[key] = value; + size_t hash_w = hasher_(key) % width_; + if (!Contains(key)) keys_count_ += 1; + for (size_t i = 0; i < data_.at(hash_w).size(); ++i) + if (data_.at(hash_w).at(i).first == key) { + data_.at(hash_w).at(i).second = value; + return; + } + data_.at(hash_w).push_back(std::pair(key, value)); + this->Rehash(); } void HashTable::Remove(const std::string& key) { - auto it = data_.find(key); - data_.erase(it); + size_t hash_w = hasher_(key) % width_; + if (!Contains(key)) throw std::out_of_range("no such key"); + keys_count_ -= 1; + for (size_t i = 0; i < data_.at(hash_w).size(); ++i) + if (data_.at(hash_w).at(i).first == key) { + data_.at(hash_w).erase(data_.at(hash_w).begin() + i); + this->Rehash(); + return; + } } -int HashTable::Find(const std::string& key) const { return data_.at(key); } +int HashTable::Find(const std::string& key) const { + size_t hash_w = hasher_(key) % width_; + for (size_t i = 0; i < data_.at(hash_w).size(); ++i) + if (data_.at(hash_w).at(i).first == key) + return data_.at(hash_w).at(i).second; +} + +size_t HashTable::Size() const { return keys_count_; } + +void HashTable::Rehash() { + if (keys_count_ < width_ * 0.8 && + (keys_count_ > width_ * 0.2 || width_ <= 20)) + return; + + HashTable new_hash_table; + if (keys_count_ >= width_ * 0.8) new_hash_table = HashTable(width_ * 2); + + for (size_t i = 0; i < width_; ++i) + for (size_t j = 0; j < data_.at(i).size(); ++j) + new_hash_table.Insert(data_.at(i).at(j).first, data_.at(i).at(j).second); -size_t HashTable::Size() const { return data_.size(); } + *this = new_hash_table; +} \ No newline at end of file diff --git a/task_06/src/hashtable.hpp b/task_06/src/hashtable.hpp index 743b3f98..4b99d7a7 100644 --- a/task_06/src/hashtable.hpp +++ b/task_06/src/hashtable.hpp @@ -2,17 +2,22 @@ #include #include -#include +#include class HashTable { public: - HashTable(); + HashTable(size_t width = 20); bool Insert(const std::string& key, int value); void InsertOrUpdate(const std::string& key, int value); void Remove(const std::string& key); int Find(const std::string& key) const; size_t Size() const; + bool Contains(const std::string& key); private: - std::unordered_map data_; + std::vector>> data_; + std::hash hasher_; + size_t keys_count_ = 0; + size_t width_ = 20; // count of vector in data + void Rehash(); };