diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 167a8fd4..5e3330f1 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.10) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + file(GLOB_RECURSE lib_source_list "src/*.cpp" "src/*.hpp") add_library(Utils ${lib_source_list}) diff --git a/lib/src/util.hpp b/lib/src/util.hpp index e69de29b..58f85b9f 100644 --- a/lib/src/util.hpp +++ b/lib/src/util.hpp @@ -0,0 +1,38 @@ +#include +#include + +// check if type comparasion operators are defined for a class +template +concept comparable = requires(CustomType a, CustomType b) { + a < b; + a <= b; + a == b; + a >= b; + a > b; + a != b; +}; + +// concept to check if the type has basic and copy constructor +template +concept constructable = requires(CustomType a, CustomType b) { + a = CustomType{}; + b = CustomType{a}; +}; + +// concept to check if the type is comparable with a concrete function +template +concept comparing = requires(CustomType a, CustomType b, Function f) { + f(a, b); +}; + +// concept to check if the type is printable +template +concept printable = requires(CustomType a) { + std::cout << a; +}; + +// concept to check if the type has a hash function tied to it +template +concept hashable = requires(K a) { + { std::hash{}(a) } -> std::convertible_to; +}; \ No newline at end of file diff --git a/task_01/README.md b/task_01/README.md index 8ceb5d4e..3e7840c5 100644 --- a/task_01/README.md +++ b/task_01/README.md @@ -1,3 +1,3 @@ # Задача 1 -Дано целое число и отсортированый массив целых чисел, нужно найти 2 числа из массива которые в сумме дадут заданное число +Дано целое число и массив целых чисел, нужно найти 2 числа из массива которые в сумме дадут заданное число \ No newline at end of file diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp index 0e4393ba..7825a349 100644 --- a/task_01/src/main.cpp +++ b/task_01/src/main.cpp @@ -1,3 +1,22 @@ #include -int main() { return 0; } +#include "solution.h" + +int main() { + int sum, arr_size, t; + std::vector input_vector; + std::unordered_map indices_map; // keeps array numbers as keys and + // indices as values behind keys + + std::cin >> sum >> arr_size; + + for (int i = 0; i < arr_size; i++) { + std::cin >> t; + input_vector.push_back(t); + } + + std::pair sol = solution(sum, input_vector); + std::cout << sol.first << ' ' << sol.second; + + return 0; +} \ No newline at end of file diff --git a/task_01/src/solution.h b/task_01/src/solution.h new file mode 100644 index 00000000..c423e0c5 --- /dev/null +++ b/task_01/src/solution.h @@ -0,0 +1,41 @@ +#include +#include + +/* + +Output - indices of two numbers, which sum is equal to needed number, if there's +no such numbers, the output is "-1 -1" + +Input: + +10 +10 +-2 2 3 3 5 8 11 13 14 15 + +Output: + +1 5 + +*/ + +// Solution below has a time complexity of O(n) and memory complexity of O(n) + +std::pair solution(int sum, std::vector v) { + std::unordered_map indices_map; // keeps array numbers as keys and + // indices as values behind keys + + for (int i = 0; i < v.size(); i++) { + if (indices_map.find(sum - v[i]) != + indices_map + .end()) { // if key "number - t" exists, we have found the solution + return {indices_map[sum - v[i]], i}; + } + + if (indices_map.find(v[i]) == indices_map.end()) + indices_map[v[i]] = + i; // We only add keys that weren't in the map before (that + // way we get the least possible sum of i and j) + } + + return {-1, -1}; // in case there are no such numbers +} \ No newline at end of file diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index ef5a86ae..2908b463 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,8 +1,39 @@ #include -#include "topology_sort.hpp" +#include "solution.h" -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] -} +TEST(solution, simple) { + std::vector v1 = {-2, 2, 3, 3, 5, 9, 11, 13, 14, 15}; + std::pair p1 = {-1, -1}; + ASSERT_EQ(p1, solution(10, v1)); + + std::vector v2 = {-2, 2, 3, 3, 5, 8, 11, 13, 14, 15}; + std::pair p2 = {1, 5}; + ASSERT_EQ(p2, solution(10, v2)); + + std::vector v3 = {}; + std::pair p3 = {-1, -1}; + ASSERT_EQ(p3, solution(0, v3)); + + std::vector v4 = {1}; + std::pair p4 = {-1, -1}; + ASSERT_EQ(p4, solution(1, v4)); + + std::vector v5 = {1, 2}; + std::pair p5 = {0, 1}; + ASSERT_EQ(p5, solution(3, v5)); + + // if there are multiple solutions, the algorithm + // will pick the one, in which sum of i and j is the least, + // where i and j are indices of numbers, which sum is equal to needed number + std::vector v6 = {1, 1, 1, 1, 1, 1, 1, 1, 1}; + std::pair p6 = {0, 1}; + ASSERT_EQ(p6, solution(2, v6)); + + // in case there are multiple solutions in which i+j is the least, + // the algorithm will pick the one, in which i is greater + std::vector v7 = {1, 2, 1, 1, 4, 5, 1, 1, 1}; + std::pair p7 = {1, 4}; + ASSERT_EQ(p7, solution(6, v7)); +} \ No newline at end of file diff --git a/task_01/src/topology_sort.cpp b/task_01/src/topology_sort.cpp deleted file mode 100644 index e53f670c..00000000 --- a/task_01/src/topology_sort.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "topology_sort.hpp" diff --git a/task_01/src/topology_sort.hpp b/task_01/src/topology_sort.hpp deleted file mode 100644 index 6f70f09b..00000000 --- a/task_01/src/topology_sort.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once 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..2b107212 100644 --- a/task_02/src/stack.hpp +++ b/task_02/src/stack.hpp @@ -1,23 +1,112 @@ #pragma once +#include #include +#include +#include #include -class Stack { +// simple structure, helps to form a linked list +template +struct Node { + Node() : prev{nullptr}, data{}, next{nullptr} {} + Node(CutsomType data_) : prev{nullptr}, data{data_}, next{nullptr} {} + void AddNode(CutsomType data_); + Node* next; + Node* prev; + CutsomType data; +}; + +// method, that allows to add a node with a value to an existing one +template +void Node::AddNode(CutsomType data_) { + next = new Node(data_); + next->prev = this; +} + +// data structure that can store and retrieve data in such fashion: +// last element inserted will be retrieved first (last in first out (LIFO)) +// insertion, deletion and retrieval has time complexity of O(1) +template +class MyStack { public: - void Push(int value); - int Pop(); + MyStack() : top_{nullptr} {} + + // insert an element at the top of the stack + void Push(CutsomType value); + + // retrieve last inserted element + CutsomType Top(); + // retrieve last inserted element and delete it from the stack + CutsomType Pop(); + // check if stack is empty + bool Empty() const { return top_ == nullptr; } private: - std::stack data_; + Node* top_; }; +template +void MyStack::Push(CutsomType value) { + if (top_ == nullptr) + top_ = new Node(value); + else { + top_->AddNode(value); + top_ = top_->next; + } +} + +template +CutsomType MyStack::Top() { + if (top_ == nullptr) throw std::runtime_error("Empty stack\n"); + return top_->data; +} + +template +CutsomType MyStack::Pop() { + if (top_ == nullptr) throw std::runtime_error("Empty stack\n"); + Node* temp = top_->prev; + CutsomType val = top_->data; + delete top_; + top_ = temp; + return val; +} + +template class MinStack { public: - void Push(int value); - int Pop(); - int GetMin(); + // insert an element at the top of the stack + void Push(CutsomType value); + // retrieve last inserted element and delete it from the stack + CutsomType Pop(); + // retrieve minimal element + CutsomType Min(); + // retrieve last inserted element + CutsomType Top(); private: - std::vector data_; + MyStack> stack; }; + +template +void MinStack::Push(CutsomType value) { + if (stack.Empty()) + stack.Push({value, value}); + else + stack.Push({value, std::min(value, stack.Top().second)}); +} + +template +CutsomType MinStack::Pop() { + return stack.Pop().first; +} + +template +CutsomType MinStack::Min() { + return stack.Top().second; +} + +template +CutsomType MinStack::Top() { + return stack.Top().first; +} \ No newline at end of file diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 54e7ce90..bdf23f8d 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -2,11 +2,22 @@ #include #include +#include #include "stack.hpp" +TEST(EmptyStack, Simple) { + MyStack stack; + EXPECT_THROW(stack.Top(), std::runtime_error); + EXPECT_THROW(stack.Pop(), std::runtime_error); + stack.Push(1); + EXPECT_NO_THROW(stack.Pop()); + EXPECT_THROW(stack.Top(), std::runtime_error); + EXPECT_THROW(stack.Pop(), std::runtime_error); +} + TEST(StackTest, Simple) { - Stack stack; + MyStack stack; stack.Push(1); // Stack [1] ASSERT_EQ(stack.Pop(), 1); // Stack [] stack.Push(1); // Stack [1] @@ -22,21 +33,30 @@ TEST(StackTest, Simple) { } TEST(MinStackTest, Simple) { - MinStack stack; + MinStack stack; stack.Push(1); // Stack [1] - ASSERT_EQ(stack.GetMin(), 1); + ASSERT_EQ(stack.Min(), 1); ASSERT_EQ(stack.Pop(), 1); // Stack [] stack.Push(1); // Stack [1] stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.GetMin(), 1); + ASSERT_EQ(stack.Min(), 1); ASSERT_EQ(stack.Pop(), 2); // Stack [1] ASSERT_EQ(stack.Pop(), 1); // Stack [] stack.Push(1); // Stack [1] stack.Push(2); // Stack [1, 2] - ASSERT_EQ(stack.GetMin(), 1); + ASSERT_EQ(stack.Min(), 1); ASSERT_EQ(stack.Pop(), 2); // Stack [1] stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.GetMin(), 1); + ASSERT_EQ(stack.Min(), 1); ASSERT_EQ(stack.Pop(), 3); // Stack [1] ASSERT_EQ(stack.Pop(), 1); // Stack [] + + EXPECT_THROW(stack.Top(), std::runtime_error); + EXPECT_THROW(stack.Pop(), std::runtime_error); +} + +TEST(StackTest, Empty) { + MyStack stack; + EXPECT_THROW(stack.Top(), std::runtime_error); + EXPECT_THROW(stack.Pop(), std::runtime_error); } \ No newline at end of file diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index ef5a86ae..fa564596 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,8 +1,33 @@ - #include -#include "topology_sort.hpp" +#include + +#include "weather.h" + +TEST(weather, Simple) { + std::vector data_vector_1 = WeatherAlgorithm({1, 2, 3, 4, 5}); + std::vector assert_vector_1 = {1, 1, 1, 1, 0}; + + ASSERT_EQ(data_vector_1, assert_vector_1); + + std::vector data_vector_2 = WeatherAlgorithm({5}); + std::vector assert_vector_2 = {0}; + + ASSERT_EQ(data_vector_2, assert_vector_2); + + std::vector data_vector_3 = WeatherAlgorithm({}); + std::vector assert_vector_3 = {}; + + ASSERT_EQ(data_vector_3, assert_vector_3); + + std::vector data_vector_4 = + WeatherAlgorithm({-1, -1, 2, 2, -5, 3, 4, 1}); + std::vector assert_vector_4 = {2, 1, 3, 2, 1, 1, 0, 0}; + + ASSERT_EQ(data_vector_4, assert_vector_4); + + std::vector data_vector_5 = WeatherAlgorithm({6, 5, 4, 3, 2, 1}); + std::vector assert_vector_5 = {0, 0, 0, 0, 0, 0}; -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + ASSERT_EQ(data_vector_5, assert_vector_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_03/src/weather.cpp b/task_03/src/weather.cpp new file mode 100644 index 00000000..adcca7e1 --- /dev/null +++ b/task_03/src/weather.cpp @@ -0,0 +1,18 @@ +#include "weather.h" + +#include + +std::vector WeatherAlgorithm(std::vector days_array) { + std::vector result(days_array.size()); + std::stack unchecked_days; + for (size_t day_index = 0; day_index < days_array.size(); day_index++) { + result[day_index] = 0; + while (!unchecked_days.empty() && + days_array[day_index] > days_array[unchecked_days.top()]) { + result[unchecked_days.top()] = day_index - unchecked_days.top(); + unchecked_days.pop(); + } + unchecked_days.push(day_index); + } + return result; +} \ No newline at end of file diff --git a/task_03/src/weather.h b/task_03/src/weather.h new file mode 100644 index 00000000..c25a98e9 --- /dev/null +++ b/task_03/src/weather.h @@ -0,0 +1,4 @@ +#pragma once +#include + +std::vector WeatherAlgorithm(std::vector days_array); \ No newline at end of file diff --git a/task_04/src/heap.hpp b/task_04/src/heap.hpp new file mode 100644 index 00000000..fd27721a --- /dev/null +++ b/task_04/src/heap.hpp @@ -0,0 +1,141 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +// data structure that allows to retrieve minimal element with O(1) time +// complexity and pop a minimal element with O(logn) time complexity +template Function = + std::function> +class Heap { + public: + // default constructor works only if your custom type is comparable! + Heap(); + // this constructor works only if your custom type is comparable with a + // function + Heap(std::function + comparing_function); + explicit Heap(std::initializer_list initializer_list); + explicit Heap(std::function + comparing_function, + std::initializer_list initializer_list); + // push an element into heap, O(logn) time complexity + void Push(CustomType element); + // pop a minimal element from heap, O(logn) time complexity + CustomType PopBottom(); + // retrieve a minimal element from heap, O(1) time complexity + CustomType Bottom(); + // return size of heap + size_t Size() { return heap_size_; } + // check if heap is empty + bool Empty() { return heap_size_ == 0; } + + private: + // bring minimal element back to place if element with given index is + // increased + void SiftDown(size_t index); + // bring minimal element back to place if element with given index is decreaed + void SiftUp(size_t index); + size_t heap_size_; + + std::function comparing_function_; + std::vector data_; +}; + +// time complexity - O(1) +template Function> +Heap::Heap() : data_{}, heap_size_{0} { + comparing_function_ = [](const CustomType& a, const CustomType& b) { + return a < b; + }; +} + +// time complexity - O(1) +template Function> +Heap::Heap( + std::function + comparing_function) + : data_{}, heap_size_{0}, comparing_function_{comparing_function} {} + +// time complexity - O(nlogn) +template Function> +Heap::Heap( + std::initializer_list initializer_list) + : Heap() { + comparing_function_ = [](const CustomType& a, const CustomType& b) { + return a < b; + }; + + for (const CustomType& value : initializer_list) Push(value); +} + +// time complexity - O(nlogn) +template Function> +Heap::Heap( + std::function + comparing_function, + std::initializer_list initializer_list) + : comparing_function_{comparing_function} { + for (const CustomType& value : initializer_list) Push(value); +} + +// time complexity - O(logn) +template Function> +void Heap::SiftDown(size_t index) { + size_t& index_1 = index; // for code to be more readable + + while (2 * index_1 + 1 < heap_size_) { + size_t left_child = 2 * index_1 + 1; + size_t right_child = 2 * index_1 + 2; + size_t index_2 = left_child; + + if ((right_child < heap_size_) && + comparing_function_(data_[right_child], data_[left_child])) + index_2 = right_child; + if (comparing_function_(data_[index_1], data_[index_2]) || + (data_[index_1] == data_[index_2])) + break; + + std::swap(data_[index_1], data_[index_2]); + index_1 = index_2; + } +} + +// time complexity - O(logn) +template Function> +void Heap::SiftUp(size_t index) { + while (comparing_function_(data_[index], data_[(index - 1) / 2])) { + std::swap(data_[index], data_[(index - 1) / 2]); + index = (index - 1) / 2; + } +} + +// time complexity - O(1) +template Function> +CustomType Heap::Bottom() { + if (Empty()) throw std::runtime_error("heap is empty"); + return data_[0]; +} + +// time complexity - O(logn) +template Function> +CustomType Heap::PopBottom() { + CustomType bottom_elem = Bottom(); + std::swap(data_[0], data_.back()); + data_.pop_back(); + heap_size_--; + SiftDown(0); + return bottom_elem; +} + +// time complexity - O(logn) +template Function> +void Heap::Push(CustomType element) { + data_.push_back(element); + heap_size_++; + if (heap_size_ >= 2) SiftUp(heap_size_ - 1); +} \ No newline at end of file diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617e..3a74a569 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,68 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include +#include +#include + +using std::string; + +#include "heap.hpp" + +TEST(Heap, Empty) { + Heap hp1; + ASSERT_EQ(hp1.Empty(), true); + ASSERT_EQ(hp1.Size(), 0); + hp1.Push(1); + ASSERT_EQ(hp1.Empty(), false); + ASSERT_EQ(hp1.Size(), 1); + ASSERT_EQ(hp1.PopBottom(), 1); + EXPECT_THROW(hp1.Bottom(), std::runtime_error); + EXPECT_THROW(hp1.PopBottom(), std::runtime_error); +} +TEST(Heap, Comparable1) { + Heap> hp2; + hp2.Push({1, 1, 1, 1, 1}); + std::array test_array_1 = {1, 1, 1, 1, 1}; + std::array test_array_2 = {1, 1, 1, 1, 0}; + ASSERT_EQ(hp2.Bottom(), test_array_1); + ASSERT_EQ(hp2.PopBottom() == test_array_2, false); +} + +TEST(Heap, WithoutDuplicates) { + Heap hp3{}; + for (int value : {3, 1, 2, 4, 5}) hp3.Push(value); + ASSERT_EQ(hp3.PopBottom(), 1); + ASSERT_EQ(hp3.PopBottom(), 2); + ASSERT_EQ(hp3.PopBottom(), 3); + ASSERT_EQ(hp3.PopBottom(), 4); + ASSERT_EQ(hp3.PopBottom(), 5); } + +TEST(Heap, WithDuplicates) { + Heap hp4{}; + for (int value : {6, 7, 7, 3, 3, 3, 1, 2}) hp4.Push(value); + ASSERT_EQ(hp4.PopBottom(), 1); + ASSERT_EQ(hp4.PopBottom(), 2); + ASSERT_EQ(hp4.PopBottom(), 3); + ASSERT_EQ(hp4.PopBottom(), 3); + ASSERT_EQ(hp4.PopBottom(), 3); + ASSERT_EQ(hp4.PopBottom(), 6); + ASSERT_EQ(hp4.PopBottom(), 7); + ASSERT_EQ(hp4.PopBottom(), 7); +} + +TEST(Heap, InitListConstructor) { + Heap hp5{1, 2, 3}; + ASSERT_EQ(hp5.PopBottom(), 1); + ASSERT_EQ(hp5.PopBottom(), 2); + ASSERT_EQ(hp5.PopBottom(), 3); +} + +TEST(StringHeap, SimpleTest) { + Heap hp6{"azz", "bzz", "zaa", "zab"}; + ASSERT_EQ(hp6.PopBottom(), "azz"); + ASSERT_EQ(hp6.PopBottom(), "bzz"); + ASSERT_EQ(hp6.PopBottom(), "zaa"); + ASSERT_EQ(hp6.PopBottom(), "zab"); +} \ No newline at end of file diff --git a/task_05/src/main.cpp b/task_05/src/main.cpp index 0e4393ba..9113f92c 100644 --- a/task_05/src/main.cpp +++ b/task_05/src/main.cpp @@ -1,3 +1,19 @@ +#include #include -int main() { return 0; } +#include "sort.hpp" + +int main() { + std::vector vector = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + for (size_t ind = 0; ind < vector.size(); ind++) + std::cout << vector[ind] << ' '; + std::cout << '\n'; + int* begin = &vector[0]; + int* end = &vector[vector.size()]; + MergeSort(begin, end); + for (size_t ind = 0; ind < vector.size(); ind++) + std::cout << vector[ind] << ' '; + std::cout << '\n'; + + return 0; +} diff --git a/task_05/src/sort.hpp b/task_05/src/sort.hpp new file mode 100644 index 00000000..6166923e --- /dev/null +++ b/task_05/src/sort.hpp @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +// function, that merges two sorted arrays, time complexity is O(n+m), +// where n - length of first array, m - length of second array +template Function = std::function< + bool(const CustomType&, const CustomType&)>> +void MergeTwoSortedArrays( + CustomType* begin, CustomType* middle, CustomType* end, + Function comparing_function = [](const CustomType& a, const CustomType& b) { + return a < b; + }) { + size_t size = end - begin; + // array where sorted data is stored temporary + CustomType array[size]; + size_t index = 0; + + CustomType* begin_reserve = begin; + CustomType* middle_reserve = middle; + + while ((begin < middle_reserve) || (middle < end)) { + if ((begin < middle_reserve) && (middle < end)) + (comparing_function(*begin, *middle)) + ? array[index++] = std::move(*(begin++)) // this line + : array[index++] = std::move(*(middle++)); // is cursed + + else if ((begin < middle_reserve) && (middle == end)) + array[index++] = std::move(*(begin++)); + + // else if for more explicit code + else if ((begin == middle_reserve) && (middle < end)) + array[index++] = std::move(*(middle++)); + } + + index = 0; + for (CustomType* iterator = begin_reserve; iterator < end; iterator++) { + *iterator = std::move(array[index]); + index++; + } +} + +// time complexity - O(nlogn) where n - length of an array +template Function = std::function< + bool(const CustomType&, const CustomType&)>> +void MergeSort( + CustomType* begin, CustomType* end, + Function comparing_function = [](CustomType a, CustomType b) { + return a < b; + }) { + if ((end == begin) || (end - begin == 1)) return; + + CustomType* middle_begin = begin + (end - begin) / 2; + MergeSort(begin, middle_begin, comparing_function); + MergeSort(middle_begin, end, comparing_function); + MergeTwoSortedArrays(begin, middle_begin, end, comparing_function); +} + +// this took way too long \ No newline at end of file diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617e..e4201111 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,156 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include +#include +#include +#include + +#include "sort.hpp" + +using std::string; + +TEST(sort, empty) { + std::vector vector1{}; + std::vector assert_vector1 = vector1; + std::sort(assert_vector1.begin(), assert_vector1.end()); + int* begin = &vector1[0]; + int* end = &vector1[vector1.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector1, assert_vector1); +} + +TEST(sort, one_elem) { + std::vector vector2 = {1}; + std::vector assert_vector2 = vector2; + std::sort(assert_vector2.begin(), assert_vector2.end()); + int* begin = &vector2[0]; + int* end = &vector2[vector2.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector2, assert_vector2); +} + +TEST(sort, two_elem) { + std::vector vector3 = {1, 2}; + std::vector assert_vector3 = vector3; + std::sort(assert_vector3.begin(), assert_vector3.end()); + int* begin = &vector3[0]; + int* end = &vector3[vector3.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector3, assert_vector3); + + std::vector vector4 = {2, 1}; + std::vector assert_vector4 = {1, 2}; + begin = &vector4[0]; + end = &vector4[vector4.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector4, assert_vector4); +} + +TEST(sort, multiple_elem_1) { + std::vector vector5 = {1, 3, 2, 5, 4}; + std::vector assert_vector5 = vector5; + std::sort(assert_vector5.begin(), assert_vector5.end()); + int* begin = &vector5[0]; + int* end = &vector5[vector5.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector5, assert_vector5); +} + +TEST(sort, multiple_elem_2) { + std::vector vector6 = {4, 3, 2, 1}; + std::vector assert_vector6 = vector6; + std::sort(assert_vector6.begin(), assert_vector6.end()); + int* begin = &vector6[0]; + int* end = &vector6[vector6.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector6, assert_vector6); +} + +TEST(sort, strings) { + std::vector vector7 = {"bb", "ba", "ab", "aa"}; + std::vector assert_vector7 = vector7; + std::sort(assert_vector7.begin(), assert_vector7.end()); + string* begin = &vector7[0]; + string* end = &vector7[vector7.size()]; + MergeSort(begin, end); + ASSERT_EQ(vector7, assert_vector7); +} + +TEST(reverse_sort, multiple_elem) { + std::vector vector8 = {1, 2, 3, 4, 5}; + std::vector assert_vector8 = vector8; + std::sort(assert_vector8.begin(), assert_vector8.end(), + [](const int& a, const int& b) { return a >= b; }); + int* begin = &vector8[0]; + int* end = &vector8[vector8.size()]; + MergeSort(begin, end, [](const int& a, const int& b) { return a >= b; }); + ASSERT_EQ(vector8, assert_vector8); +} + +bool sum_comparasion(std::vector a, std::vector b) { + int sum1 = 0; + for (size_t ind = 0; ind < a.size(); ind++) sum1 += a[ind]; + int sum2 = 0; + for (size_t ind = 0; ind < b.size(); ind++) sum2 += b[ind]; + return sum1 < sum2; +} + +TEST(sum_sort, multiple_elem) { + std::vector> vector9 = {{5, 1, 1}, {4, 4, 4}, {1, 1, 0}}; + std::vector> assert_vector9 = vector9; + std::sort(assert_vector9.begin(), assert_vector9.end()); + std::vector* begin = &vector9[0]; + std::vector* end = &vector9[vector9.size()]; + MergeSort(begin, end, sum_comparasion); + EXPECT_FALSE(vector9 == assert_vector9); + std::sort(assert_vector9.begin(), assert_vector9.end(), sum_comparasion); + EXPECT_TRUE(vector9 == assert_vector9); +} + +/* + + This test falls at compile time + | + | + | + V + +*/ + +// TEST(wrong_sort, non_function) { +// std::vector vector10 = {1, 2, 3}; +// int quote_unquote_function = 1; +// int* begin = &vector10[0]; +// int* end = &vector10[vector10.size()]; +// EXPECT_ANY_THROW(MergeSort(begin, end, quote_unquote_function)); +// } + +struct A { + bool test_variable = false; +}; + +/* + + This test falls at compile time + | + | + | + V + +*/ + +// TEST(wrong_sort, uncomparable_types) +// { +// std::vector vector11 = {{}, {}, {}}; +// A* begin = &vector11[0]; +// A* end = &vector11[vector11.size()]; +// MergeSort(begin, end); +// } + +TEST(wrong_sort, uncomparable_types) { + std::vector vector12 = {{}, {}, {}}; + A* begin = &vector12[0]; + A* end = &vector12[vector12.size()]; + MergeSort(begin, end, [](const A& a, const A& b) { return true; }); + // this doesn't make sense but it works } diff --git a/task_06/src/k_ordinal_statistics.hpp b/task_06/src/k_ordinal_statistics.hpp new file mode 100644 index 00000000..24ac9884 --- /dev/null +++ b/task_06/src/k_ordinal_statistics.hpp @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include +#include + +template +size_t Partition(std::vector& data_array, size_t left_index, + size_t right_index) { + // Seperate by the middle element + CustomType& separator = data_array[(left_index + right_index) / 2]; + size_t left = left_index; + size_t right = right_index; + while (left <= right) { + while (data_array[left] < separator) left++; + while (data_array[right] > separator) right--; + if (left >= right) break; + std::swap(data_array[left++], data_array[right--]); + } + return right; +} + +template +CustomType FindOrdinalStatistic(std::vector data_array, size_t k) { + if (k >= data_array.size()) + throw std::runtime_error("k is greater than the size of the array\n"); + size_t left = 0; + size_t right = data_array.size() - 1; + for (;;) { + size_t middle = Partition(data_array, left, right); + + if (middle == k) + return data_array[middle]; + else if (k < middle) + right = middle; + else + left = middle + 1; + } +} \ No newline at end of file diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617e..3add0c50 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,50 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include +#include + +#include "k_ordinal_statistics.hpp" + +using std::string; + +TEST(k_ordinal_statistics, wrong_data) { + std::vector vector1 = {}; + EXPECT_THROW(FindOrdinalStatistic(vector1, 0), std::runtime_error); +} + +TEST(k_ordinal_statistics, already_sorted) { + std::vector vector2 = {1, 2, 3, 4, 5, 6}; + int answer2_1 = 1; + int answer2_2 = 4; + int answer2_3 = 6; + ASSERT_EQ(answer2_1, FindOrdinalStatistic(vector2, 0)); + ASSERT_EQ(answer2_2, FindOrdinalStatistic(vector2, 3)); + ASSERT_EQ(answer2_3, FindOrdinalStatistic(vector2, 5)); } + +TEST(k_ordinal_statistics, unsorted_1) { + std::vector vector3 = {10, 5, 6, 3, 4, 1, 2, 7, 8, 9}; + int answer3_1 = 5; + int answer3_2 = 10; + int answer3_3 = 1; + ASSERT_EQ(answer3_1, FindOrdinalStatistic(vector3, 4)); + ASSERT_EQ(answer3_2, FindOrdinalStatistic(vector3, 9)); + ASSERT_EQ(answer3_3, FindOrdinalStatistic(vector3, 0)); +} + +TEST(k_ordinal_statistics, unsorted_2) { + std::vector vector4 = {111, 201, 50, -10, -405}; + int answer4_1 = 50; + int answer4_2 = -405; + ASSERT_EQ(answer4_1, FindOrdinalStatistic(vector4, 2)); + ASSERT_EQ(answer4_2, FindOrdinalStatistic(vector4, 0)); +} + +TEST(k_ordinal_statistics, string_array) { + std::vector vector5 = {"bbb", "bab", "aaa", "caa", "cab"}; + string answer5_1 = "aaa"; + string answer5_2 = "bbb"; + ASSERT_EQ(answer5_1, FindOrdinalStatistic(vector5, 0)); + ASSERT_EQ(answer5_2, FindOrdinalStatistic(vector5, 2)); +} \ No newline at end of file diff --git a/task_07/src/splay_tree.hpp b/task_07/src/splay_tree.hpp new file mode 100644 index 00000000..47710551 --- /dev/null +++ b/task_07/src/splay_tree.hpp @@ -0,0 +1,349 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "tree_node.hpp" + +enum class ParentsType : char { + orphan, + left, + right, + left_left, + right_right, + left_right, + right_left +}; + +template +class SplayTree { + public: + SplayTree() : tree_root_{nullptr} {} + SplayTree(TreeNode>* tree_root) + : tree_root_{tree_root} {} + // time complexity - O(nlogn) + explicit SplayTree( + std::initializer_list> init_list); + + // time complexity - O(logn) + CustomType& operator[](Key key); + // time complexity - O(logn) + void Merge(SplayTree& tree); + // time complexity - O(logn) + SplayTree Split(Key key); + // time complexity - O(logn) + void Add(Key key, CustomType value); + // time complexity - O(logn) + void Remove(Key key); + // time complexity - O(1) + TreeNode>* Root() { return tree_root_; } + // time complexity - O(logn) + Key RightestKey(); + + private: + enum class Direction : bool { left, right }; + + // time complexity - O(logn) + TreeNode>* Find(Key key); + // time complexity - O(1) + ParentsType ParentsCheck(TreeNode>* x_node); + + // time complexity - O(logn) + void Splay(Key key); + + // time complexity - O(1) + void Zig(TreeNode>* x_node, Direction direction); + + // time complexity - O(1) + void ZigZig(TreeNode>* x_node, + Direction direction); + + // time complexity - O(1) + void ZigZag(TreeNode>* x_node, + Direction direction); + + TreeNode>* tree_root_; +}; + +// time complexity - O(logn) +template +TreeNode>* SplayTree::Find( + Key key) { + if (tree_root_ == nullptr) return nullptr; + + TreeNode>* current_node = tree_root_; + for (;;) { + if (key == current_node->value.first) + return current_node; + else if (key > current_node->value.first) { + if (current_node->right_child == nullptr) return current_node; + + current_node = current_node->right_child; + } + // else if for more explicit code + else if (key < current_node->value.first) { + if (current_node->left_child == nullptr) return current_node; + + current_node = current_node->left_child; + } + } +} + +// time complexity - O(nlogn) +template +SplayTree::SplayTree( + std::initializer_list> init_list) { + for (const std::pair& pair : init_list) + Add(pair.first, pair.second); +} + +// time complexity - O(logn) +template +Key SplayTree::RightestKey() { + if (tree_root_ == nullptr) throw std::runtime_error("empty tree\n"); + TreeNode>* current_node = tree_root_; + + while (current_node->right_child != nullptr) + current_node = current_node->right_child; + + return current_node->value.first; +} + +// time complexity - O(1) +template +void SplayTree::Zig( + TreeNode>* x_node, Direction direction) { + TreeNode>* parent_node = x_node->parent; + TreeNode>* parent_of_a_parent = + parent_node->parent; + + if (direction == Direction::right) { + TreeNode>* x_right_node = x_node->right_child; + + parent_node->left_child = x_right_node; + if (x_right_node != nullptr) x_right_node->parent = parent_node; + x_node->right_child = parent_node; + parent_node->parent = x_node; + } + // else if for more explicit code + else if (direction == Direction::left) { + TreeNode>* x_left_node = x_node->left_child; + + parent_node->right_child = x_left_node; + if (x_left_node != nullptr) x_left_node->parent = parent_node; + x_node->left_child = parent_node; + parent_node->parent = x_node; + } + + x_node->parent = parent_of_a_parent; + if (x_node->is_root()) tree_root_ = x_node; +} + +// time complexity - O(1) +template +void SplayTree::ZigZig( + TreeNode>* x_node, Direction direction) { + if (direction == Direction::right) { + Zig(x_node->parent, Direction::right); + Zig(x_node, Direction::right); + } + // else if for more explicit code + else if (direction == Direction::left) { + Zig(x_node->parent, Direction::left); + Zig(x_node, Direction::left); + } + if (x_node->is_root()) tree_root_ = x_node; +} + +// time complexity - O(1) +template +void SplayTree::ZigZag( + TreeNode>* x_node, Direction direction) { + if (direction == Direction::right) { + Zig(x_node, Direction::right); + Zig(x_node, Direction::left); + } + // else if for more explicit code + else if (direction == Direction::left) { + Zig(x_node, Direction::left); + Zig(x_node, Direction::right); + } + if (x_node->is_root()) tree_root_ = x_node; +} + +// time complexity - O(1) +template +ParentsType SplayTree::ParentsCheck( + TreeNode>* x_node) { + if (x_node == nullptr) + throw std::logic_error("x_node is a nullptr\n"); + else if (x_node->parent == nullptr) + return ParentsType::orphan; + else if (x_node->parent != nullptr) { + TreeNode>* parent_node = x_node->parent; + if (parent_node->parent == nullptr) { + if (parent_node->left_child == parent_node->right_child) + throw std::logic_error("left and right children are the same node!?\n"); + else if (parent_node->left_child == x_node) + return ParentsType::right; + // else if for more explicit code + else if (parent_node->right_child == x_node) + return ParentsType::left; + } else { + if (parent_node->parent->left_child == parent_node->parent->right_child) + throw std::logic_error("left and right children are the same node!?\n"); + if (parent_node->left_child == x_node) { + if (parent_node->parent->left_child == parent_node) + return ParentsType::right_right; + // else if for more explicit code + else if (parent_node->parent->right_child == parent_node) + return ParentsType::right_left; + } + // else if for more explicit code + else if (parent_node->right_child == x_node) { + if (parent_node->parent->left_child == parent_node) + return ParentsType::left_right; + // else if for more explicit code + else if (parent_node->parent->right_child == parent_node) + return ParentsType::left_left; + } + } + } + throw std::logic_error( + "got throw all else if blocks but haven't found a match\n"); +} + +// time complexity - O(logn) +template +void SplayTree::Splay(Key key) { + if (tree_root_ == nullptr) return; + + TreeNode>* x_node = Find(key); + + while (!x_node->is_root()) { + ParentsType parents_type = ParentsCheck(x_node); + + switch (parents_type) { + case ParentsType::left: + Zig(x_node, Direction::left); + break; + case ParentsType::right: + Zig(x_node, Direction::right); + break; + case ParentsType::left_left: + ZigZig(x_node, Direction::left); + break; + case ParentsType::right_right: + ZigZig(x_node, Direction::right); + break; + case ParentsType::left_right: + ZigZag(x_node, Direction::left); + break; + case ParentsType::right_left: + ZigZag(x_node, Direction::right); + break; + case ParentsType::orphan: + break; + } + } +} + +// time complexity - O(logn) +template +void SplayTree::Merge(SplayTree& tree) { + if (tree_root_ == nullptr) { + tree_root_ = tree.tree_root_; + return; + } else if (tree.tree_root_ == nullptr) + return; + Splay(RightestKey()); + tree_root_->right_child = tree.tree_root_; + tree.tree_root_->parent = tree_root_; +} + +// time complexity - O(logn) +template +SplayTree SplayTree::Split(Key key) { + Splay(key); + if (tree_root_ == nullptr) return SplayTree{nullptr}; + if (Find(key) != tree_root_) + throw std::logic_error("key is not root after splaying"); + + if (tree_root_->value.first >= key) { + SplayTree right_tree{tree_root_}; + tree_root_ = tree_root_->left_child; + + if (tree_root_ != nullptr) tree_root_->parent = nullptr; + right_tree.tree_root_->left_child = nullptr; + return right_tree; + } else { + SplayTree right_tree{tree_root_->right_child}; + + tree_root_->right_child = nullptr; + if (right_tree.tree_root_ != nullptr) + right_tree.tree_root_->parent = nullptr; + return right_tree; + } +} + +// time complexity - O(logn) +template +void SplayTree::Add(Key key, CustomType value) { + SplayTree right_tree = Split(key); + + TreeNode>* new_root = + new TreeNode>{ + tree_root_, {key, value}, right_tree.tree_root_}; + + tree_root_ = new_root; +} + +// time complexity - O(logn) +template +void SplayTree::Remove(Key key) { + TreeNode>* x_node = Find(key); + if (x_node->value.first != key) + throw std::runtime_error("removing a non-existing element\n"); + + Splay(key); + x_node = tree_root_; + tree_root_ = x_node->left_child; + if (tree_root_ != nullptr) tree_root_->parent = nullptr; + + x_node->left_child = nullptr; + SplayTree right_tree{x_node->right_child}; + x_node->right_child = nullptr; + if (right_tree.tree_root_ != nullptr) right_tree.tree_root_->parent = nullptr; + + delete x_node; + + Merge(right_tree); +} + +// time complexity - O(logn) +template +CustomType& SplayTree::operator[](Key key) { + TreeNode>* x_node = Find(key); + + if ((x_node == nullptr) || (x_node->value.first != key)) + Add(key, CustomType{}); + TreeNode>* value_node = Find(key); + + return value_node->value.second; +} + +// time complexity - O(n) +template +void in_order_DFS(TreeNode>* current_node, + std::vector& data_vector) { + if (current_node == nullptr) return; + if (current_node->parent == nullptr && !data_vector.empty()) + throw std::runtime_error("data_vector is not empty\n"); + + in_order_DFS(current_node->left_child, data_vector); + data_vector.push_back(current_node->value.second); + in_order_DFS(current_node->right_child, data_vector); +} \ No newline at end of file diff --git a/task_07/src/test.cpp b/task_07/src/test.cpp index 5e11617e..ad11009e 100644 --- a/task_07/src/test.cpp +++ b/task_07/src/test.cpp @@ -1,6 +1,64 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "splay_tree.hpp" + +TEST(SplayTree, Simple) { + SplayTree st1{}; + st1.Add(1, 1); + st1.Add(2, 2); + st1.Add(3, 3); + + EXPECT_TRUE(st1.Root()->parent == nullptr); + + ASSERT_EQ(st1[1], 1); + ASSERT_EQ(st1[2], 2); + ASSERT_EQ(st1[3], 3); +} + +TEST(in_order_DFS, Simple) { + SplayTree st2{}; + for (int i = 10; i >= 1; i--) st2[i] = i * i; + std::vector vector2; + std::vector assert_vector2 = {1, 4, 9, 16, 25, 36, 49, 64, 81, 100}; + in_order_DFS(st2.Root(), vector2); + + EXPECT_TRUE(st2.Root()->parent == nullptr); + ASSERT_EQ(vector2, assert_vector2); +} + +TEST(tree_sort, Simple) { + SplayTree st3{}; + std::vector data_vector1 = {2, 3, 1, 4, 5}; + for (const int& value : data_vector1) st3[value] = value; + std::vector vector3; + std::vector assert_vector3 = {1, 2, 3, 4, 5}; + in_order_DFS(st3.Root(), vector3); + + EXPECT_TRUE(st3.Root()->parent == nullptr); + ASSERT_EQ(vector3, assert_vector3); } + +TEST(string_tree, Simple) { + SplayTree st4{}; + st4[1] = "hello"; + st4[5] = "this"; + st4[10] = "is"; + st4[11] = "string"; + st4[20] = "tree"; + + ASSERT_EQ(st4[1], "hello"); + ASSERT_EQ(st4[5], "this"); + ASSERT_EQ(st4[10], "is"); + ASSERT_EQ(st4[11], "string"); + ASSERT_EQ(st4[20], "tree"); +} + +TEST(string_tree, String_keys) { + SplayTree st5{}; + st5["this"] = "is"; + st5["even"] = "crazier"; + + ASSERT_EQ(st5["this"], "is"); + ASSERT_EQ(st5["even"], "crazier"); +} \ No newline at end of file diff --git a/task_07/src/tree_node.hpp b/task_07/src/tree_node.hpp new file mode 100644 index 00000000..25de7d99 --- /dev/null +++ b/task_07/src/tree_node.hpp @@ -0,0 +1,44 @@ +#pragma once +#include + +template +struct TreeNode { + TreeNode() + : left_child{nullptr}, parent{nullptr}, right_child{nullptr}, value{} {} + TreeNode(CustomType value_) + : left_child{nullptr}, + parent{nullptr}, + right_child{nullptr}, + value{value_} {} + TreeNode(TreeNode* parent_) + : left_child{nullptr}, parent{parent_}, right_child{nullptr}, value{} {} + TreeNode(CustomType value_, TreeNode& parent_) + : left_child{nullptr}, + parent{parent_}, + right_child{nullptr}, + value{value_} {} + TreeNode(TreeNode* left_child_, CustomType value_, TreeNode* right_child_) + : left_child{left_child_}, value{value_}, right_child{right_child_} { + if (left_child_ != nullptr) left_child_->parent = this; + if (right_child_ != nullptr) right_child_->parent = this; + } + + void add_left(CustomType value_); + void add_right(CustomType value_); + bool is_root() { return this->parent == nullptr; } + + TreeNode* left_child; + TreeNode* parent; + TreeNode* right_child; + CustomType value; +}; + +template +void TreeNode::add_left(CustomType value_) { + left_child = new TreeNode(value_, *this); +} + +template +void TreeNode::add_right(CustomType value_) { + right_child = new TreeNode(value_, *this); +} \ No newline at end of file diff --git a/task_08/src/hashtable.hpp b/task_08/src/hashtable.hpp new file mode 100644 index 00000000..52a63ada --- /dev/null +++ b/task_08/src/hashtable.hpp @@ -0,0 +1,233 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "linkedlist.hpp" + +template +class HashTable { + public: + HashTable() + : array_length_{base_length_}, + hash_vector_{base_length_}, + number_of_elements_{0}, + fillness_ratio_{0}, + default_value_{} {} + explicit HashTable(T default_value) + : array_length_{base_length_}, + hash_vector_{base_length_}, + number_of_elements_{0}, + fillness_ratio_{0}, + default_value_{default_value} {} + HashTable(std::initializer_list> initializer_list); + + void SetDefault(T default_value); + + std::vector> Items(); + std::vector Keys(); + std::vector Values(); + + T& operator[](const K& key); + T& Pop(K key); + bool HasKey(K key); + + void Clear(); + void Print(); + + private: + enum rehash_type { up = 1, down = 2 }; + void Rehash(rehash_type type); + + std::vector>> hash_vector_{base_length_}; + std::hash hasher_; + size_t array_length_; + size_t number_of_elements_; + double fillness_ratio_; + T default_value_{}; + + constexpr static double critical_ratio_ = 0.95; + constexpr static size_t base_length_ = 8; +}; + +template +HashTable::HashTable( + std::initializer_list> initializer_list) { + double relative_length = initializer_list.size() / (double)base_length_; + number_of_elements_ = 0; + if (initializer_list.size() <= 8) + array_length_ = base_length_; + else + array_length_ = + base_length_ * std::pow(2, std::ceil(std::log2(relative_length))); + + hash_vector_ = {}; + hash_vector_.resize(array_length_); + default_value_ = {}; + + for (const std::pair* i = initializer_list.begin(); + i < initializer_list.end(); i++) { + std::pair* key_value = const_cast*>(i); + K& key = key_value->first; + T& value = key_value->second; + LinkedList>& key_list = + hash_vector_[hasher_(key) % array_length_]; + + bool exist_flag = false; + for (size_t i = 0; i < key_list.size(); i++) { + if (key_list[i].first == key) { + exist_flag = true; + break; + } + } + + if (!exist_flag) { + key_list.push_back({key, value}); + number_of_elements_++; + } else + throw std::runtime_error("inserted two pairs with the same key"); + } + fillness_ratio_ = number_of_elements_ / (double)array_length_; +} + +template +void HashTable::SetDefault(T default_value) { + default_value_ = default_value; +} + +template +T& HashTable::operator[](const K& key) { + LinkedList>& key_list = + hash_vector_[(hasher_(key)) % array_length_]; + + if (key_list.Empty()) { + key_list.PushBack({key, default_value_}); + + number_of_elements_++; + fillness_ratio_ = number_of_elements_ / (double)array_length_; + + if (fillness_ratio_ > critical_ratio_) { + Rehash(rehash_type::up); + return (*this)[key]; + } + + return key_list[0].second; + } else { + for (size_t i = 0; i < key_list.Size(); i++) { + if (key_list[i].first == key) return key_list[i].second; + } + + key_list.PushBack({key, default_value_}); + + number_of_elements_++; + fillness_ratio_ = number_of_elements_ / (double)array_length_; + + if (fillness_ratio_ > critical_ratio_) { + Rehash(rehash_type::up); + return (*this)[key]; + } + + return key_list.Back().second; + } +} + +template +T& HashTable::Pop(K key) { + LinkedList>& key_list = + hash_vector_[(hasher_(key)) % array_length_]; + for (size_t i = 0; i < key_list.Size(); i++) { + if (key_list[i].first == key) { + T& value = key_list.Pop(i).second; + number_of_elements_--; + fillness_ratio_ = number_of_elements_ / (double)array_length_; + if (fillness_ratio_ < critical_ratio_ / 4) Rehash(rehash_type::down); + return value; + } + } + throw std::runtime_error("out_of_range"); +} + +template +bool HashTable::HasKey(K key) { + LinkedList>& key_list = + hash_vector_[hasher_(key) % array_length_]; + for (size_t i = 0; i < key_list.Size(); i++) + if (key_list[i].first == key) return true; + return false; +} + +template +void HashTable::Print() { + for (size_t i = 0; i < hash_vector_.size(); i++) { + for (size_t j = 0; j < hash_vector_[i].Size(); j++) { + std::cout << hash_vector_[i][j].second << ' '; + } + std::cout << '\n'; + } +} + +template +std::vector> HashTable::Items() { + std::vector> vector; + + for (size_t i = 0; i < hash_vector_.size(); i++) + for (size_t j = 0; j < hash_vector_[i].Size(); j++) + vector.push_back(hash_vector_[i][j]); + + return vector; +} + +template +std::vector HashTable::Keys() { + std::vector vector; + + for (size_t i = 0; i < hash_vector_.size(); i++) + for (size_t j = 0; j < hash_vector_[i].Size(); j++) + vector.push_back(hash_vector_[i][j].first); + + return vector; +} + +template +std::vector HashTable::Values() { + std::vector vector; + + for (size_t i = 0; i < hash_vector_.size(); i++) + for (size_t j = 0; j < hash_vector_[i].Size(); j++) + vector.push_back(hash_vector_[i][j].second); + + return vector; +} + +template +void HashTable::Clear() { + hash_vector_.clear(); + hash_vector_.resize(base_length_); + array_length_ = base_length_; + + number_of_elements_ = 0; + fillness_ratio_ = 0; +} + +template +void HashTable::Rehash(rehash_type type) { + std::vector> vector = Items(); + + hash_vector_.clear(); + array_length_ = + (type == rehash_type::up) ? array_length_ * 2 : array_length_ / 4; + hash_vector_.resize(array_length_); + + for (size_t i = 0; i < vector.size(); i++) { + K& key = vector[i].first; + T& value = vector[i].second; + LinkedList>& key_list = + hash_vector_[hasher_(key) % array_length_]; + + key_list.PushBack({key, value}); + } + fillness_ratio_ = number_of_elements_ / (double)array_length_; +} diff --git a/task_08/src/linkedlist.hpp b/task_08/src/linkedlist.hpp new file mode 100644 index 00000000..476819ca --- /dev/null +++ b/task_08/src/linkedlist.hpp @@ -0,0 +1,172 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +template +struct Node { + Node() : next{nullptr}, data{} {} + Node(T data_) : next{nullptr}, data{data_} {} + void AddNode(T data_); + Node* next; + T data; +}; + +template +void Node::AddNode(T data_) { + next = new Node(data_); +} + +template +struct LinkedList { + LinkedList() : head_{nullptr}, tail_{nullptr}, size_{0} {} + LinkedList(std::initializer_list initializer_list); + ~LinkedList(); + LinkedList(const LinkedList& ll); + + void PushBack(T elem); + + bool Empty() const { return size_ == 0; } + size_t Size() const { return size_; } + + T& operator[](size_t index); + T& Back(); + T& Pop(size_t index); + + void Print(); + + private: + Node* head_; + Node* tail_; + size_t size_; +}; + +template +LinkedList::~LinkedList() { + if (size_ != 0) { + Node* curr = head_; + Node* deletable = curr; + for (size_t i = 0; i < size_; i++) { + curr = curr->next; + delete deletable; + deletable = curr; + } + } +} + +template +LinkedList::LinkedList(std::initializer_list initializer_list) { + size_ = initializer_list.size(); + if (size_ == 0) { + head_ = nullptr; + tail_ = nullptr; + } else { + head_ = new Node{*initializer_list.begin()}; + Node* curr = head_; + bool first_iteration_flag = 1; + for (const T& value : initializer_list) { + if (first_iteration_flag) + first_iteration_flag = 0; + else { + curr->AddNode(value); + curr = curr->next; + } + } + tail_ = curr; + } +} + +template +LinkedList::LinkedList(const LinkedList& ll) { + size_ = ll.Size(); + head_ = new Node(head_->data); + Node* curr = ll.head_; + Node* curr_copy = head_; + for (size_t i = 1; i < ll.Size(); i++) { + curr = curr->next; + curr_copy->AddNode(curr->data); + curr_copy = curr_copy->next; + } + tail_ = curr_copy; +} + +template +void LinkedList::PushBack(T elem) { + if (tail_ != nullptr) { + tail_->AddNode(elem); + tail_ = tail_->next; + } else { + tail_ = new Node(elem); + head_ = tail_; + } + size_++; +} + +template +T& LinkedList::operator[](size_t index) { + if (index >= size_) throw std::runtime_error("index is out of range"); + Node* curr = head_; + for (size_t i = 0; i < index; i++) { + curr = curr->next; + } + return curr->data; +} + +template +T& LinkedList::Back() { + if (tail_ == nullptr) + throw std::runtime_error( + "can't use method \"Back()\": the linked list is empty"); + return tail_->data; +} + +template +T& LinkedList::Pop(size_t index) { + if (index >= size_) throw std::runtime_error("index out of range"); + + if (size_ == 1) { + T& value = head_->data; + delete head_; + head_ = nullptr; + tail_ = nullptr; + size_--; + return value; + } + + if (index == 0) { + T& value = head_->data; + Node* prev = head_; + head_ = head_->next; + delete prev; + size_--; + return value; + } + + Node* prev = nullptr; + Node* curr = head_; + + for (size_t i = 0; i < index; i++) { + prev = curr; + curr = curr->next; + } + + prev->next = curr->next; + T& value = curr->data; + delete curr; + curr = nullptr; + size_--; + return value; +} + +template +void LinkedList::Print() { + Node* curr = head_; + while (curr != nullptr) { + std::cout << curr->data << ' '; + curr = curr->next; + } +} \ No newline at end of file diff --git a/task_08/src/main.cpp b/task_08/src/main.cpp index 0e4393ba..9ec81e03 100644 --- a/task_08/src/main.cpp +++ b/task_08/src/main.cpp @@ -1,3 +1,3 @@ #include -int main() { return 0; } +int main() { return 0; } \ No newline at end of file diff --git a/task_08/src/test.cpp b/task_08/src/test.cpp index 5e11617e..1b2256f5 100644 --- a/task_08/src/test.cpp +++ b/task_08/src/test.cpp @@ -1,6 +1,86 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] -} +#include +#include + +#include "hashtable.hpp" + +TEST(hashtable, simple) { + HashTable hash_table_1; + std::vector assert_vector_1; + + for (int i = 1; i <= 64; i++) { + hash_table_1[i] = i * i; + assert_vector_1.push_back(i * i); + } + + std::vector assert_vector_2; + for (int i = 1; i <= 64; i++) assert_vector_2.push_back(hash_table_1[i]); + + std::vector assert_vector_3 = hash_table_1.Values(); + std::sort(assert_vector_3.begin(), assert_vector_3.end()); + + // check if we don't lose any data when inserting into hash_table + ASSERT_EQ(assert_vector_1, assert_vector_2); + ASSERT_EQ(assert_vector_1, assert_vector_3); + + for (int i = 17; i <= 64; i++) hash_table_1.Pop(i); + + std::vector assert_vector_4; + std::vector assert_vector_5; + + for (int i = 1; i <= 16; i++) { + assert_vector_4.push_back(i * i); + assert_vector_5.push_back((hash_table_1[i])); + } + + std::vector assert_vector_6 = hash_table_1.Values(); + std::sort(assert_vector_6.begin(), assert_vector_6.end()); + + // check if we don't lose any data when deleting elements from hash_table + ASSERT_EQ(assert_vector_4, assert_vector_5); + ASSERT_EQ(assert_vector_4, assert_vector_6); + + std::vector assert_vector_7; + std::vector assert_vector_8; + + for (int i = 1; i <= 16; i++) { + assert_vector_7.push_back(hash_table_1.HasKey(i)); + assert_vector_8.push_back(true); + } + + for (int i = 17; i <= 64; i++) { + assert_vector_7.push_back(hash_table_1.HasKey(i)); + assert_vector_8.push_back(false); + } + + // check if don't have "phantom" elements after deletion + ASSERT_EQ(assert_vector_7, assert_vector_8); + + hash_table_1.Clear(); + + std::vector assert_vector_9; + std::vector assert_vector_10; + + for (int i = 1; i <= 64; i++) { + assert_vector_9.push_back(hash_table_1.HasKey(i)); + assert_vector_10.push_back(false); + } + + // check the method "Clear()" + ASSERT_EQ(assert_vector_9, assert_vector_10); + + hash_table_1.SetDefault(16); + + std::vector assert_vector_11; + std::vector assert_vector_12; + + for (int i = 1; i <= 16; i++) { + assert_vector_11.push_back(hash_table_1[i]); + assert_vector_12.push_back(16); + } + + // check default value + ASSERT_EQ(assert_vector_11, assert_vector_12); +} \ 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 00000000..ce6d278a --- /dev/null +++ b/task_09/src/solution.hpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +long long CoinChange(long long amount, std::vector coins) { + if (amount == 0) return 0; + if (amount < 0) return -1; + if (coins.empty()) return -1; + std::sort(coins.begin(), coins.end()); + std::vector dp(amount + 1); + + for (int current_amount = 0; current_amount < amount + 1; current_amount++) + dp[current_amount] = 0; + + for (int current_amount = 1; current_amount < dp.size(); current_amount++) { + for (int coin_index = coins.size() - 1; coin_index >= 0; coin_index--) { + if (current_amount - coins[coin_index] == 0) + dp[current_amount] = 1; + else if (current_amount - coins[coin_index] > 0 && + dp[current_amount - coins[coin_index]] != 0) { + if (dp[current_amount] == 0) + dp[current_amount] = dp[current_amount - coins[coin_index]] + 1; + else + dp[current_amount] = std::min( + dp[current_amount], dp[current_amount - coins[coin_index]] + 1); + } + } + } + // return -1 if it's impossible to get this amount with given coins + return dp[amount] == 0 ? -1 : dp[amount]; +} \ No newline at end of file diff --git a/task_09/src/test.cpp b/task_09/src/test.cpp index 869094dd..df746bea 100644 --- a/task_09/src/test.cpp +++ b/task_09/src/test.cpp @@ -1,4 +1,48 @@ #include -TEST(TopologySort, Simple) { ASSERT_EQ(1, 1); } +#include + +#include "solution.hpp" + +TEST(MoneyTest, Simple_1) { + long long numbah1 = 14; + std::vector nominals1 = {1, 2, 5, 10}; + ASSERT_EQ(3, CoinChange(numbah1, nominals1)); +} + +TEST(MoneyTest, Simple_2) { + long long numbah2 = 19; + std::vector nominals2 = {1, 2, 5, 10}; + ASSERT_EQ(4, CoinChange(numbah2, nominals2)); +} + +TEST(MoneyTest, Simple_3) { + long long numbah3 = 31; + std::vector nominals3 = {10}; + ASSERT_EQ(-1, CoinChange(numbah3, nominals3)); +} + +TEST(MoneyTest, Wrong_numbah) { + long long numbah4 = -1; + std::vector nominals4 = {1, 2, 3}; + ASSERT_EQ(-1, CoinChange(numbah4, nominals4)); +} + +TEST(MoneyTest, Empty_array) { + long long numbah5 = 40; + std::vector nominals5 = {}; + ASSERT_EQ(-1, CoinChange(numbah5, nominals5)); +} + +TEST(MoneyTest, Suggested_Test_1) { + long long numbah6 = 14; + std::vector nominals6 = {1, 2, 5, 7, 10}; + ASSERT_EQ(2, CoinChange(numbah6, nominals6)); +} + +TEST(MoneyTest, Suggested_Test_2) { + long long numbah7 = 10; + std::vector nominals7 = {1, 2, 5, 7}; + ASSERT_EQ(2, CoinChange(numbah7, nominals7)); +} \ No newline at end of file