diff --git a/CMakeLists.txt b/CMakeLists.txt index 06c6f26..cc9a45a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(homeworks) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sandbox) +#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sandbox) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/additional_tasks) diff --git a/README.md b/README.md index 39762f8..8b13789 100644 --- a/README.md +++ b/README.md @@ -1,7 +1 @@ -# Домашнее задание для 2 семестра алгоритмов и структур данных -### Для удобства можно пользоваться папкой lib, все файлы из этой папки будут подключаться к любой задаче - -### Можно получить дополнительные баллы, если добавить интересные текстовые задачи. Необходимы текст задачи, решение и тесты. - -### Можно получить дополнительные баллы, если добавить теорию в папку doc. Делается в отдельном ПР diff --git a/additional_tasks/graph_view/CMakeLists.txt b/additional_tasks/graph_view/CMakeLists.txt new file mode 100644 index 0000000..d903dc4 --- /dev/null +++ b/additional_tasks/graph_view/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.10) + +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) + +file(GLOB_RECURSE source_list "src/*.cpp" "src/*.hpp") +file(GLOB_RECURSE lib_source_list "../lib/src/*.cpp" "../lib/src/*.hpp") +file(GLOB_RECURSE main_source_list "src/main.cpp") +file(GLOB_RECURSE test_source_list "src/*.cpp") +file(GLOB_RECURSE test_list "src/*test.cpp") + +list(REMOVE_ITEM test_source_list ${main_source_list}) +list(REMOVE_ITEM source_list ${test_list}) + +include_directories(${PROJECT_NAME} PUBLIC src) +include_directories(${PROJECT_NAME} PUBLIC ../lib/src) + +add_executable(${PROJECT_NAME} ${source_list} ${lib_source_list}) + +# Locate GTest +enable_testing() +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(${PROJECT_NAME}_tests ${test_source_list}) +target_link_libraries( + ${PROJECT_NAME}_tests + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}_tests) diff --git a/additional_tasks/graph_view/README.md b/additional_tasks/graph_view/README.md new file mode 100644 index 0000000..1ef8d9b --- /dev/null +++ b/additional_tasks/graph_view/README.md @@ -0,0 +1,10 @@ +# Задача на перевод одного и того же графа в его различные представления + +В качестве возможных вариантов хранения графа используются: + +1. Матрица смежности, основанная на двумерном bool векторе +2. Лист смежности, основанный на двумерном size_t векторе +3. Лист смежности, основанных на векторе из unordered_set (то есть без повторения вершин) +4. Список вершин, основанный на векторе пар + +Перевод осуществляется из любого представления в любое. \ No newline at end of file diff --git a/additional_tasks/graph_view/src/container_couts.hpp b/additional_tasks/graph_view/src/container_couts.hpp new file mode 100644 index 0000000..ce38f1e --- /dev/null +++ b/additional_tasks/graph_view/src/container_couts.hpp @@ -0,0 +1,51 @@ +#pragma + +#include +#include +#include +#include + +template +std::ostream& operator<< (std::ostream& ost, std::pair& v) +{ + ost << '[' << v.first << "," << v.second << ']'; + return ost; +} + +template +std::ostream& operator<< (std::ostream& ost, const std::vector& v) +{ + ost << '['; + if(!v.empty()) + { + auto it = v.begin(); + ost << *it; + ++it; + while(it != v.end()) + { + ost << ',' << *it; + ++it; + } + } + ost << ']'; + return ost; +} + +template +std::ostream& operator<< (std::ostream& ost, const std::unordered_set& v) +{ + ost << '{'; + if(!v.empty()) + { + auto it = v.begin(); + ost << *it; + ++it; + while(it != v.end()) + { + ost << ',' << *it; + ++it; + } + } + ost << '}'; + return ost; +} diff --git a/additional_tasks/graph_view/src/graph_view.cpp b/additional_tasks/graph_view/src/graph_view.cpp new file mode 100644 index 0000000..bbb5dbb --- /dev/null +++ b/additional_tasks/graph_view/src/graph_view.cpp @@ -0,0 +1,183 @@ +#include "graph_view.hpp" +#include "container_couts.hpp" + +AdjacencyMatrix& AdjacencyMatrix::operator= (const AdjacencyListVec& vec_list) +{ + data_.clear(); + data_.resize(vec_list.size(), std::vector(vec_list.size())); + for (size_t vec_index = 0; vec_index < vec_list.size(); vec_index++) + for (size_t index = 0; index < vec_list[vec_index].size(); index++) + data_[vec_index][vec_list[vec_index][index]] = vec_list[vec_index][index] || vec_list[vec_index][index] == 0 ? true : false; + return *this; +} + +AdjacencyMatrix& AdjacencyMatrix::operator= (const AdjacencyListUnorderedSet& unord_list) +{ + data_.clear(); + data_.resize(unord_list.size(), std::vector(unord_list.size())); + for (size_t vec_index = 0; vec_index < unord_list.size(); vec_index++) + for (size_t elem : unord_list[vec_index]) + data_[vec_index][elem] = elem || elem == 0 ? true : false; + return *this; +} + +AdjacencyMatrix& AdjacencyMatrix::operator= (const EdgeList& edge_list) +{ + data_.clear(); + size_t max_vertex{0}; + for (size_t index = 0; index < edge_list.size(); index++) + { + std::pair pair = edge_list[index]; + size_t parent = pair.first; + size_t child = pair.second; + max_vertex = std::max(max_vertex, std::max(parent, child) + 1); + } + + data_.resize(max_vertex, std::vector(max_vertex)); + + for (size_t index = 0; index < edge_list.size(); index++) + { + std::pair pair = edge_list[index]; + size_t parent = pair.first; + size_t child = pair.second; + size_t max_vertex = std::max(parent, child) + 1; + data_[parent][child] = child || child == 0 ? true : false; + } + return *this; +} + + + + +AdjacencyListVec& AdjacencyListVec::operator= (const AdjacencyMatrix& matrix) +{ + data_.clear(); + data_.resize(matrix.size()); + for (size_t vertex = 0; vertex < matrix.size(); vertex++) + for (size_t index = 0; index < matrix.size(); index++) + if(matrix[vertex][index]) data_[vertex].push_back(index); + return *this; +} + +AdjacencyListVec& AdjacencyListVec::operator= (const AdjacencyListUnorderedSet& list) +{ + AdjacencyMatrix matrix(3); + matrix = list; + *this = matrix; + return *this; +} + +AdjacencyListVec& AdjacencyListVec::operator= (const EdgeList& list) +{ + data_.clear(); + size_t max_vertex{0}; + for (size_t index = 0; index < list.size(); index++) + { + std::pair pair = list[index]; + size_t parent = pair.first; + size_t child = pair.second; + max_vertex = std::max(max_vertex, std::max(parent, child) + 1); + } + data_.resize(max_vertex); + for (size_t index = 0; index < list.size(); index++) + { + std::pair pair = list[index]; + size_t parent = pair.first; + size_t child = pair.second; + data_[parent].push_back(child); + } + return *this; +} + + + + +AdjacencyListUnorderedSet& AdjacencyListUnorderedSet::operator= (const AdjacencyMatrix& matrix) +{ + data_.clear(); + data_.resize(matrix.size()); + for (size_t vertex = 0; vertex < matrix.size(); vertex++) + for (size_t index = 0; index < matrix.size(); index++) + if(matrix[vertex][index]) data_[vertex].insert(index); + return *this; +} + +AdjacencyListUnorderedSet& AdjacencyListUnorderedSet::operator= (const AdjacencyListVec& list) +{ + AdjacencyMatrix matrix(3); + matrix = list; + *this = matrix; + return *this; +} + +AdjacencyListUnorderedSet& AdjacencyListUnorderedSet::operator= (const EdgeList& list) +{ + AdjacencyMatrix matrix(3); + matrix = list; + *this = matrix; + return *this; +} + + + + +EdgeList& EdgeList::operator= (const AdjacencyMatrix& matrix) +{ + data_.clear(); + for (size_t vertex = 0; vertex < matrix.size(); vertex++) + for (size_t index = 0; index < matrix.size(); index++) + if(matrix[vertex][index]) data_.push_back(std::pair (vertex, index)); + return *this; +} + +EdgeList& EdgeList::operator= (const AdjacencyListVec& list) +{ + data_.clear(); + for(size_t parent = 0; parent < list.size(); parent++) + { + for(size_t child : list[parent]) + data_.push_back(std::pair(parent, child)); + } + return *this; +} + +EdgeList& EdgeList::operator= (const AdjacencyListUnorderedSet& list) +{ + AdjacencyMatrix matrix(3); + matrix = list; + *this = matrix; + return *this; +} + + + + + +std::ostream& operator<< (std::ostream& ost, AdjacencyMatrix& matrix) +{ + for (auto& vec : matrix.data_) + std::cout << vec << std::endl; + return ost; +} + +std::ostream& operator<< (std::ostream& ost, AdjacencyListVec& list_vec) +{ + size_t i = 0; + for (auto& vec : list_vec.data_) + std::cout << i++ << ":" << vec << std::endl; + return ost; +} + +std::ostream& operator<< (std::ostream& ost, AdjacencyListUnorderedSet& list_unord_set) +{ + for (auto& unord_set : list_unord_set.data_) + std::cout << unord_set << std::endl; + return ost; +} + +std::ostream& operator<< (std::ostream& ost, EdgeList& edge_list) +{ + for (auto& pair : edge_list.data_) + std::cout << pair << std::endl; + return ost; +} diff --git a/additional_tasks/graph_view/src/graph_view.hpp b/additional_tasks/graph_view/src/graph_view.hpp new file mode 100644 index 0000000..5b90ba7 --- /dev/null +++ b/additional_tasks/graph_view/src/graph_view.hpp @@ -0,0 +1,138 @@ +#ifndef GRAPH_VIEWS_H +#define GRAPH_VIEWS_H + +#include +#include +#include +#include + +class AdjacencyMatrix; +class AdjacencyListVec; +class AdjacencyListUnorderedSet; +class EdgeList; + +class AdjacencyMatrix +{ +private: + std::vector> data_; +public: + AdjacencyMatrix(const size_t size) : data_(size, std::vector(size, 0)) {} + AdjacencyMatrix(const std::vector> data) : data_{data} {} + + const std::vector& operator[] (const size_t index) const { return data_[index]; } + std::vector& operator[] (const size_t index) { return data_[index]; } + + friend std::ostream& operator<< (std::ostream &ost, AdjacencyMatrix& matrix); + + AdjacencyMatrix& operator= (const AdjacencyListVec& vec_list); + AdjacencyMatrix& operator= (const AdjacencyListUnorderedSet& list_unord); + AdjacencyMatrix& operator= (const EdgeList& edge_list); + + size_t size() const { return data_.size(); } + + const std::vector>::iterator begin() { return data_.begin(); } + const std::vector>::iterator end() { return data_.end(); } + + std::vector> GetMatrix() const { return data_; } + void LoadMatrix(const std::vector> new_data) { data_ = new_data; }; +}; + +class AdjacencyListVec +{ +private: + std::vector> data_; +public: + AdjacencyListVec(const size_t size) : data_(size) {} + AdjacencyListVec(const std::vector> data) : data_{data} {} + + const std::vector& operator[] (const size_t index) const { return data_[index]; } + std::vector& operator[] (const size_t index) { return data_[index]; } + + friend std::ostream& operator<< (std::ostream &ost, AdjacencyListVec& list_vec); + + bool operator() (const size_t parent, const size_t child) const{ + const std::vector& vec = data_[parent]; + return std::find(vec.begin(), vec.end(), child) != vec.end(); + } + + // add edge between node parent and child + void AddEdge(const size_t parent, const size_t child) { data_[parent].push_back(child); } + size_t size() const { return data_.size(); } + + AdjacencyListVec& operator= (const AdjacencyMatrix& matrix); + AdjacencyListVec& operator= (const AdjacencyListUnorderedSet& list); + AdjacencyListVec& operator= (const EdgeList& list); + + const std::vector>::iterator begin() { return data_.begin(); } + const std::vector>::iterator end() { return data_.end(); } + + std::vector> GetList() const { return data_; } + void LoadList(const std::vector> new_data) { data_ = new_data; }; +}; + +class AdjacencyListUnorderedSet +{ +private: + std::vector> data_; +public: + AdjacencyListUnorderedSet(const size_t size) : data_(size) {} + AdjacencyListUnorderedSet(const std::vector> data) : data_{data} {} + + const std::unordered_set& operator[] (const size_t index) const { return data_[index]; } + std::unordered_set& operator[] (const size_t index) { return data_[index]; } + + friend std::ostream& operator<< (std::ostream &ost, AdjacencyListUnorderedSet& list_unord_set); + bool operator() (const size_t parent, const size_t child){ + const std::unordered_set& unord_set = data_[parent]; + return std::find(unord_set.begin(), unord_set.end(), child) != unord_set.end(); + } + + // add edge between node parent and child + void AddEdge(const size_t parent, const size_t child) { data_[parent].insert(child); } + size_t size() const { return data_.size(); } + + AdjacencyListUnorderedSet& operator= (const AdjacencyMatrix& matrix); + AdjacencyListUnorderedSet& operator= (const AdjacencyListVec& list); + AdjacencyListUnorderedSet& operator= (const EdgeList& list); + + + const std::vector>::iterator begin() { return data_.begin(); } + const std::vector>::iterator end() { return data_.end(); } + + std::vector> GetList() const { return data_; } + void LoadList(const std::vector> new_data) { data_ = new_data; }; +}; + +class EdgeList +{ +private: + std::vector> data_; +public: + EdgeList(const size_t size) : data_(size) {} + EdgeList(const std::vector> data) : data_{data} {} + + friend std::ostream& operator<< (std::ostream &ost, EdgeList& edge_list); + bool operator() (const std::pair edge) { return std::find(data_.begin(), data_.end(), edge) != data_.end(); } + bool operator() (const size_t parent, const size_t child) { + std::pair edge(parent, child); + return std::find(data_.begin(), data_.end(), edge) != data_.end(); + } + + // add edge between node parent and child + void AddEdge(const size_t parent, const size_t child) { data_.push_back(std::pair (parent, child)); } + void AddEdge(const std::pair edge) { data_.push_back(edge); } + size_t size() const { return data_.size(); } + const std::pair& operator[] (const size_t index) const { return data_[index]; } + + EdgeList& operator= (const AdjacencyMatrix& matrix); + EdgeList& operator= (const AdjacencyListVec& list); + EdgeList& operator= (const AdjacencyListUnorderedSet& list); + + const std::vector>::iterator begin() { return data_.begin(); } + const std::vector>::iterator end() { return data_.end(); } + + std::vector> GetList() const { return data_; } + void LoadList(const std::vector> new_data) { data_ = new_data; }; +}; + +#endif // GRAPH_VIEWS_H diff --git a/additional_tasks/graph_view/src/main.cpp b/additional_tasks/graph_view/src/main.cpp new file mode 100644 index 0000000..9ec81e0 --- /dev/null +++ b/additional_tasks/graph_view/src/main.cpp @@ -0,0 +1,3 @@ +#include + +int main() { return 0; } \ No newline at end of file diff --git a/additional_tasks/graph_view/src/test.cpp b/additional_tasks/graph_view/src/test.cpp new file mode 100644 index 0000000..76bcbfb --- /dev/null +++ b/additional_tasks/graph_view/src/test.cpp @@ -0,0 +1,216 @@ +#include + +#include "graph_view.hpp" + +using namespace testing; + +TEST(Graphs_view_test_system, Adjacency_matrix_base) +{ + AdjacencyMatrix x(5); + x[0][1] = 1; + ASSERT_EQ(x[2][3], 0); + ASSERT_EQ(x[0][1], 1); + AdjacencyMatrix y(std::vector> { + {0,1,1,0,1,0}, + {1,1,1,0,0,0}, + {0,0,1,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}}); + ASSERT_EQ(y[0][3], 0); + ASSERT_EQ(y[1][1], 1); +} + +TEST(Graphs_view_test_system, Adjacency_matrix_convertion) +{ + AdjacencyMatrix matrix_1(3); + AdjacencyMatrix matrix_2(3); + AdjacencyMatrix matrix_3(3); + AdjacencyListUnorderedSet unord_set_list(std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}, + {}, + }); + AdjacencyListVec vec_list(std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}, + {}, + }); + EdgeList edge_list(std::vector> + {{0,1},{0,2},{0,4},{1,0},{1,1},{1,2},{1,2},{2,2}}); + + matrix_1 = vec_list; + std::vector> result_vec{ + {0,1,1,0,1,0}, + {1,1,1,0,0,0}, + {0,0,1,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}}; + ASSERT_EQ(matrix_1.GetMatrix(), result_vec); + + matrix_2 = unord_set_list; + ASSERT_EQ(matrix_2.GetMatrix(), result_vec); + + matrix_3 = edge_list; + result_vec = std::vector>{ + {0,1,1,0,1}, + {1,1,1,0,0}, + {0,0,1,0,0}, + {0,0,0,0,0}, + {0,0,0,0,0}}; + ASSERT_EQ(matrix_3.GetMatrix(), result_vec); +} + +TEST(Graphs_view_test_system, Adjacency_list_vec_convertion) +{ + AdjacencyMatrix matrix(std::vector>{ + {0,1,1,0,1,0}, + {0,1,1,0,0,0}, + {0,0,1,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}}); + AdjacencyListVec vec_list_1(3); + vec_list_1 = matrix; + std::vector> result_vec{ + {1,2,4}, + {1,2}, + {2}, + {}, + {}, + {}}; + ASSERT_EQ(vec_list_1.GetList(), result_vec); + + AdjacencyListVec vec_list_2(3); + AdjacencyListUnorderedSet unord_set_list( + std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}, + {}}); + vec_list_2 = unord_set_list; + result_vec = std::vector>{ + {1,2,4}, + {0,1,2}, + {2}, + {}, + {}, + {}}; + ASSERT_EQ(vec_list_2.GetList(), result_vec); + + AdjacencyListVec vec_list_3(3); + EdgeList edge_list(std::vector> + {{0,1},{0,2},{0,4},{1,0},{1,1},{1,2},{1,2},{2,2}}); + vec_list_3 = edge_list; + result_vec = std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}}; + ASSERT_EQ(vec_list_3.GetList(), result_vec); +} + +TEST(Graphs_view_test_system, Adjacency_list_unordered_set_convertion) +{ + AdjacencyListUnorderedSet unord_set_list_1(3); + + AdjacencyMatrix matrix(std::vector>{ + {0,1,1,0,1,0}, + {0,1,1,0,0,0}, + {0,0,1,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}}); + unord_set_list_1 = matrix; + std::vector> result_unord_set{ + {1,2,4}, + {1,2}, + {2}, + {}, + {}, + {}}; + ASSERT_EQ(unord_set_list_1.GetList(), result_unord_set); + + AdjacencyListUnorderedSet unord_set_list_2(3); + AdjacencyListVec vec_list(std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}, + {}}); + unord_set_list_2 = vec_list; + result_unord_set = std::vector>{ + {1,2,4}, + {0,1,2}, + {2}, + {}, + {}, + {}}; + ASSERT_EQ(unord_set_list_2.GetList(), result_unord_set); + + AdjacencyListUnorderedSet unord_set_list_3(3); + EdgeList edge_list(std::vector> + {{0,1},{0,2},{0,4},{1,0},{1,1},{1,2},{1,2},{2,2}}); + unord_set_list_3 = edge_list; + result_unord_set = std::vector>{ + {1,2,4}, + {0,1,2}, + {2}, + {}, + {}}; + ASSERT_EQ(unord_set_list_3.GetList(), result_unord_set); +} + +TEST(Graphs_view_test_system, Edge_list_convertion) +{ + AdjacencyMatrix matrix(std::vector>{ + {0,1,1,0,1,0}, + {0,1,1,0,0,0}, + {0,0,1,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}, + {0,0,0,0,0,0}}); + EdgeList edge_list_1(3); + edge_list_1 = matrix; + std::vector> result_list( + {{0,1},{0,2},{0,4},{1,1},{1,2},{2,2}}); + ASSERT_EQ(edge_list_1.GetList(), result_list); + + AdjacencyListVec vec_list = (std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}, + {}}); + EdgeList edge_list_2(3); + edge_list_2 = vec_list; + result_list = std::vector>( + {{0,1},{0,2},{0,4},{1,0},{1,1},{1,2},{1,2},{2,2}}); + ASSERT_EQ(edge_list_2.GetList(), result_list); + + AdjacencyListUnorderedSet unord_set_list( + std::vector>{ + {1,2,4}, + {0,1,2,2}, + {2}, + {}, + {}, + {}}); + EdgeList edge_list_3(3); + edge_list_3 = unord_set_list; + result_list = std::vector>( + {{0,1},{0,2},{0,4},{1,0},{1,1},{1,2},{2,2}}); + ASSERT_EQ(edge_list_3.GetList(), result_list); +} diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index ef5a86a..149fbc0 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -4,5 +4,19 @@ #include "topology_sort.hpp" TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] + Vertex v0(0); + Vertex v1(1); + Vertex v2(2); + Vertex v3(3); + AddEdge(&v1, &v0); + AddEdge(&v2, &v0); + AddEdge(&v3, &v0); + + Graph graph; + graph.AddRoot(&v1); + graph.AddRoot(&v2); + graph.AddRoot(&v3); + + std::vector answer = {1, 0, 2, 3}; + ASSERT_EQ(graph.TopologySort(), answer); } diff --git a/task_01/src/topology_sort.cpp b/task_01/src/topology_sort.cpp index e53f670..4e046df 100644 --- a/task_01/src/topology_sort.cpp +++ b/task_01/src/topology_sort.cpp @@ -1 +1,62 @@ +#include + #include "topology_sort.hpp" + +void Graph::AddRoot(Vertex* root) +{ + this->_roots.push_back(root); +} + +void Graph::AddElement(int number, std::vector parents) +{ + if (parents.empty()) + throw std::runtime_error("Vertex has no parents"); + + Vertex v = Vertex(number, parents); +} + +std::vector Graph::TopologySort() +{ + if (this->_roots.empty()) + throw std::runtime_error("Graph is empty. Nothing to sort"); + + std::vector result; + for(Vertex* root : this->_roots) + { + root->ChangeColour(GRAY); + std::vector current_sorting = this->_TopologySort(root); + root->ChangeColour(BLACK); + result.push_back(root->GetNumber()); + result.insert(result.end(), current_sorting.begin(), current_sorting.end()); + } + return result; +} + +std::vector Graph::_TopologySort(Vertex* root) +{ + std::vector result; + + for (Vertex* vertex : root->GetChildren()) + { + Colour vertex_colour = vertex->GetColour(); + if(vertex_colour == WHITE) + { + vertex->ChangeColour(GRAY); + std::vector current_sorting = this->_TopologySort(vertex); + vertex->ChangeColour(BLACK); + result.push_back(vertex->GetNumber()); + result.insert(result.end(), current_sorting.begin(), current_sorting.end()); + } + else if (vertex_colour == BLACK) + continue; + else if (vertex_colour == GRAY) + throw std::runtime_error("There is a cycle in the graph"); + } + return result; +} + +void AddEdge(Vertex* root, Vertex* child) +{ + child->AddParent(root); + root->AddChild(child); +} \ No newline at end of file diff --git a/task_01/src/topology_sort.hpp b/task_01/src/topology_sort.hpp index 6f70f09..e58af00 100644 --- a/task_01/src/topology_sort.hpp +++ b/task_01/src/topology_sort.hpp @@ -1 +1,59 @@ #pragma once + +#include +#include + +/* +Taryan algotirthm is used there: https://ru.wikipedia.org/wiki/Топологическая_сортировка +In these interpreatation all vertices can be: + 1) "white" (if not visited) + 2) "black" (if visited) + 3) "gray" (if gone in but not gone out) +*/ + +enum Colour +{ + WHITE, + GRAY, + BLACK, +}; + +class Vertex +{ + /* Struct of a vertex in a graph */ + + public: + Vertex(int number, std::vector parents = std::vector()): + _parents{parents}, _number{number} {} + + std::vector GetParents() {return _parents; } + std::vector GetChildren() { return _children; } + Colour GetColour() { return _colour; } + int GetNumber() { return _number; } + + void AddChild(Vertex* children) { _children.push_back(children); } + void ChangeColour(Colour new_colour) { this->_colour = new_colour; } + void AddParent(Vertex* parent) { _parents.push_back(parent); } + + private: + int _number; + std::vector _parents; + std::vector _children; + Colour _colour = WHITE; // possbile colours are "white", "gray" and "'black" +}; + +class Graph +{ + public: + Graph() = default; + + void AddRoot(Vertex* root); // add new root to the graph + void AddElement(int number, std::vector parents); // add new element to the graph + std::vector TopologySort(); // get sorted vertices + private: + std::vector _TopologySort(Vertex* vertex); // recursion of topology sort for this vertex + std::vector _roots; // vector with all roots in graph + // size_t _cur_number = 1; // number of last added vertex +}; + +void AddEdge(Vertex* root, Vertex* child); \ No newline at end of file diff --git a/task_02/src/graph.cpp b/task_02/src/graph.cpp new file mode 100644 index 0000000..20c54fd --- /dev/null +++ b/task_02/src/graph.cpp @@ -0,0 +1,43 @@ +#include "graph.hpp" + +void Graph::Dfs(size_t vertex, size_t parent) +{ + timer_++; + tin_[vertex] = timer_; + ret_[vertex] = tin_[vertex]; + used_[vertex] = true; + size_t children = 0; + for (auto to : data_[vertex]) + { + if (to == parent) + continue; + if (used_[to]) + ret_[vertex] = std::min(ret_[vertex], tin_[to]); + else + { + Dfs(to, vertex); + ret_[vertex] = std::min(ret_[vertex], ret_[to]); + children += 1; // for root case + if (ret_[to] == tin_[to]) + bridges_.push_back(std::pair (vertex, to)); + if (ret_[to] >= tin_[vertex] and parent != -1) + dots_.push_back(vertex); + } + if (parent == -1 && children > 1) // it's root + dots_.push_back(vertex); + } +} + +std::vector FindRouters(Graph& g) +{ + if (g.Empty()) + g.FindBridgesAndDots(); + return g.GetDotes(); +} + +std::vector> FindWires(Graph& g) +{ + if (g.Empty()) + g.FindBridgesAndDots(); + return g.GetBridges(); +} diff --git a/task_02/src/graph.hpp b/task_02/src/graph.hpp new file mode 100644 index 0000000..948ca3a --- /dev/null +++ b/task_02/src/graph.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + + +class Graph +{ +private: + std::vector> data_; + size_t timer_; + std::vector tin_; // times of enter in vertex + std::vector ret_; // ret_[v] = min(tin_[v], tin_[u]), where u - vertex before v (it can be not reacheable from v) + std::vector used_; // visited vertexes + + std::vector dots_; // articulation points + std::vector> bridges_; + + void Dfs(size_t vertex, size_t parent=-1); + bool calculation_done = false; // if dots and bridges have already found + +public: + Graph(const size_t size) : data_(size), tin_(size), ret_(size), used_(size) {} + Graph(const std::vector> data) : data_{data}, tin_(data.size()), ret_(data.size()), used_(data.size()) {} + + std::vector operator[] (const size_t index) const { return data_[index]; } + + // check if edge exist between parent and child + bool operator() (const size_t parent, const size_t child) const{ + const std::vector& vec = data_[parent]; + return std::find(vec.begin(), vec.end(), child) != vec.end(); + } + + // add edge between node parent and child + void AddEdge(const size_t parent, const size_t child) { data_[parent].push_back(child); } + size_t size() const { return data_.size(); } + + const std::vector>::iterator begin() { return data_.begin(); } + const std::vector>::iterator end() { return data_.end(); } + + std::vector> GetList() const { return data_; } + void LoadList(const std::vector> new_data) { data_ = new_data; }; + + void FindBridgesAndDots() { Dfs(0); }; + std::vector> GetBridges() { return bridges_; }; + std::vector GetDotes() { return dots_; }; + + bool Empty() { return !calculation_done; }; +}; + +std::vector FindRouters(Graph& g); +std::vector> FindWires(Graph& g); \ No newline at end of file diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 5e11617..be06910 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,6 +1,30 @@ #include +#include "graph.hpp" +#include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(Routers, Simple) { + Graph g(6); + g.AddEdge(0, 1); + g.AddEdge(1, 2); + g.AddEdge(2, 0); + g.AddEdge(2, 3); + g.AddEdge(3, 4); + g.AddEdge(4, 5); + g.AddEdge(5, 3); + std::vector answer = {3, 2}; + ASSERT_EQ(FindRouters(g), answer); // Stack [] } + +TEST(Bridges, Simple) { + Graph g(6); + g.AddEdge(0, 1); + g.AddEdge(1, 2); + g.AddEdge(2, 0); + g.AddEdge(2, 3); + g.AddEdge(3, 4); + g.AddEdge(4, 5); + g.AddEdge(5, 3); + std::vector> answer = {std::pair{2, 3}}; + ASSERT_EQ(FindWires(g), answer); // Stack [] +} \ No newline at end of file diff --git a/task_03/src/algo.cpp b/task_03/src/algo.cpp new file mode 100644 index 0000000..7a12a77 --- /dev/null +++ b/task_03/src/algo.cpp @@ -0,0 +1,95 @@ +#include "algo.hpp" + +// Bellman-Ford algorithm to find shortest path from source to all other vertices +std::vector Graph::BellmanFord(int source) { + std::vector distance(vertex_count, INF); + distance[source] = 0; + + // Relax all edges |V| - 1 times + for (int i = 0; i < vertex_count - 1; ++i) { + for (const auto& edge : edges) { + int u = edge.source; + int v = edge.destination; + int w = edge.weight; + if (distance[u] != INF && distance[u] + w < distance[v]) { + distance[v] = distance[u] + w; + } + } + } + + // Check for negative-weight cycles + for (const auto& edge : edges) { + int u = edge.source; + int v = edge.destination; + int w = edge.weight; + if (distance[u] != INF && distance[u] + w < distance[v]) { + std::cout << "Graph contains negative-weight cycle!\n"; + return {}; + } + } + + return distance; +} + +// Dijkstra's algorithm to find shortest path from given source vertex to all other vertices +std::vector Graph::Dijkstra(int source) { + std::vector distance(vertex_count, INF); + distance[source] = 0; + + auto compare = [](const std::pair& a, const std::pair& b) { + return a.second > b.second; + }; + std::priority_queue, std::vector>, decltype(compare)> pq(compare); + pq.push({source, 0}); + + while (!pq.empty()) { + int u = pq.top().first; + pq.pop(); + + for (const auto& edge : edges) { + if (edge.source == u) { + int v = edge.destination; + int w = edge.weight; + if (distance[u] != INF && distance[u] + w < distance[v]) { + distance[v] = distance[u] + w; + pq.push({v, distance[v]}); + } + } + } + } + + return distance; +} + +// Johnson's algorithm to find shortest paths between all pairs of vertices +std::vector> Graph::JohnsonsAlgorithm() { + // Add an extra vertex with zero-weight edges to all other vertices + for (int i = 0; i < vertex_count; ++i) + edges.push_back({vertex_count, i, 0}); + + // Run Bellman-Ford algorithm from the extra vertex + std::vector phi = BellmanFord(vertex_count); + + std::vector> distance_matrix(vertex_count, std::vector(vertex_count, INF)); + + // Re-weight the edges + for (auto& edge : edges) { + int u = edge.source; + int v = edge.destination; + int w = edge.weight; + if (phi[u] != INF && phi[u] != INF) { + edge.weight = w + phi[u] - phi[v]; + } + } + + // Run Dijkstra's algorithm for every vertex + for (int i = 0; i < vertex_count; ++i) { + std::vector distance = Dijkstra(i); + for (int j = 0; j < vertex_count; ++j) { + if (distance[j] != INF) + distance_matrix[i][j] = distance[j] + phi[j] - phi[i]; + } + } + + return distance_matrix; +} \ No newline at end of file diff --git a/task_03/src/algo.hpp b/task_03/src/algo.hpp new file mode 100644 index 0000000..8481f75 --- /dev/null +++ b/task_03/src/algo.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + +#define INF std::numeric_limits::max() + +// Structure to represent a weighted edge +struct Edge { + int source; + int destination; + int weight; +}; + +// Graph class +class Graph { +private: + int vertex_count; // Number of vertices + std::vector edges; // Vector to store graph edges + +public: + Graph(int V) : vertex_count(V) {} + + // Add an edge to the graph + void AddEdge(int source, int destination, int weight) { + edges.push_back({source, destination, weight}); + } + + std::vector BellmanFord(int source); + std::vector Dijkstra(int source); + std::vector> JohnsonsAlgorithm(); +}; diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index 5e11617..4b29173 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,6 +1,25 @@ - #include +#include "algo.hpp" + +#define INF std::numeric_limits::max() + +TEST(JohnsonTest, Simple) { + Graph graph(5); + graph.AddEdge(0, 1, 5); // Add an edge from vertex 0 to vertex 1 with weight 5 + graph.AddEdge(0, 3, 3); // Add an edge from vertex 0 to vertex 3 with weight 3 + graph.AddEdge(1, 2, 2); // Add an edge from vertex 1 to vertex 2 with weight 2 + graph.AddEdge(2, 3, 6); // Add an edge from vertex 2 to vertex 3 with weight 6 + graph.AddEdge(3, 4, 4); // Add an edge from vertex 3 to vertex 4 with weight 4 + graph.AddEdge(4, 1, 1); // Add an edge from vertex 4 to vertex 1 with weight 1 + + std::vector> expected_distances = { + {0, 5, 7, 3, 7}, + {INF, 0, 2, 8, 12}, + {INF, 11, 0, 6, 10}, + {INF, 5, 7, 0, 4}, + {INF, 1, 3, 9, 0} + }; -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] -} + std::vector> distances = graph.JohnsonsAlgorithm(); + ASSERT_EQ(distances, expected_distances); +} \ No newline at end of file diff --git a/task_04/src/dijkstra.cpp b/task_04/src/dijkstra.cpp new file mode 100644 index 0000000..10debf8 --- /dev/null +++ b/task_04/src/dijkstra.cpp @@ -0,0 +1,41 @@ +#include "dijkstra.hpp" + +// Dijkstra's algorithm to find shortest path from given source vertex to all other vertices +std::vector Graph::Dijkstra_(int source) { + std::vector distance(vertex_count, INF); + distance[source] = 0; + + auto compare = [](const std::pair& a, const std::pair& b) { + return a.second > b.second; + }; + std::priority_queue, std::vector>, decltype(compare)> pq(compare); + pq.push({source, 0}); + + while (!pq.empty()) { + int u = pq.top().first; + pq.pop(); + + for (const auto& edge : edges) { + if (edge.source == u) { + int v = edge.destination; + int w = edge.weight; + if (distance[u] != INF && distance[u] + w < distance[v]) { + distance[v] = distance[u] + w; + pq.push({v, distance[v]}); + } + } + } + } + + return distance; +} + +std::vector> Graph::Dijkstra() +{ + std::vector> distance_matrix(vertex_count, std::vector(vertex_count, INF)); + + for (int i = 0; i < vertex_count; ++i) + distance_matrix[i] = Dijkstra_(i); + + return distance_matrix; +} \ No newline at end of file diff --git a/task_04/src/dijkstra.hpp b/task_04/src/dijkstra.hpp new file mode 100644 index 0000000..55a336c --- /dev/null +++ b/task_04/src/dijkstra.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +#define INF std::numeric_limits::max() + +// Structure to represent a weighted edge +struct Edge { + int source; + int destination; + int weight; +}; + +// Graph class +class Graph { +private: + int vertex_count; // Number of vertices + std::vector edges; // Vector to store graph edges + std::vector Dijkstra_(int source); + +public: + Graph(int V) : vertex_count(V) {} + + // Add an edge to the graph + void AddEdge(int source, int destination, int weight) { + edges.push_back({source, destination, weight}); + } + + std::vector> Dijkstra(); +}; \ No newline at end of file diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..ea23354 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,23 @@ - +#include "dijkstra.hpp" #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(Dijkstra, Simple) { + Graph graph(5); + graph.AddEdge(0, 1, 5); // Add an edge from vertex 0 to vertex 1 with weight 5 + graph.AddEdge(0, 3, 3); // Add an edge from vertex 0 to vertex 3 with weight 3 + graph.AddEdge(1, 2, 2); // Add an edge from vertex 1 to vertex 2 with weight 2 + graph.AddEdge(2, 3, 6); // Add an edge from vertex 2 to vertex 3 with weight 6 + graph.AddEdge(3, 4, 4); // Add an edge from vertex 3 to vertex 4 with weight 4 + graph.AddEdge(4, 1, 1); // Add an edge from vertex 4 to vertex 1 with weight 1 + + std::vector> expected_distances = { + {0, 5, 7, 3, 7}, + {INF, 0, 2, 8, 12}, + {INF, 11, 0, 6, 10}, + {INF, 5, 7, 0, 4}, + {INF, 1, 3, 9, 0} + }; + + std::vector> distances = graph.Dijkstra(); + ASSERT_EQ(distances, expected_distances); } diff --git a/task_05/src/rmq.hpp b/task_05/src/rmq.hpp new file mode 100644 index 0000000..1ea4ed2 --- /dev/null +++ b/task_05/src/rmq.hpp @@ -0,0 +1,23 @@ +#include + +class RMQ +{ + private: + std::vector> data_; + public: + RMQ(std::vector input) : data_(std::__lg(input.size() + 1), std::vector(input.size())) + { + const size_t n = input.size(); + std::copy(input.begin(), input.end(), data_[0].begin()); + + for (int l = 0; l < std::__lg(n); l++) + for (int i = 0; i + (2 << l) <= n; i++) + data_[l+1][i] = std::min(data_[l][i], data_[l][i + (1 << l)]); + } + + int rmq(const size_t l, const size_t r) const + { + const size_t power_2 = std::__lg(r - l); + return std::min(data_[power_2][l], data_[power_2][r - (1 << power_2) + 1]); + } +}; diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617..30ee568 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,12 @@ - +#include "rmq.hpp" #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(RMQ, Simple) { + std::vector data{0, 2, 3, 5, 1, 4, 7}; + RMQ rmq(data); + ASSERT_EQ(rmq.rmq(1, 4), 1); + ASSERT_EQ(rmq.rmq(0, 5), 0); + ASSERT_EQ(rmq.rmq(1, 6), 1); + ASSERT_EQ(rmq.rmq(5, 6), 4); + ASSERT_EQ(rmq.rmq(4, 4), 1); } diff --git a/task_06/src/lca.hpp b/task_06/src/lca.hpp new file mode 100644 index 0000000..a67cf3a --- /dev/null +++ b/task_06/src/lca.hpp @@ -0,0 +1,85 @@ +#pragma once + + +#include +#include +#include + +using namespace std; + +// Class to represent the binary tree +class BinaryTree { + int n; // Number of nodes in the tree + vector parent; // Parent array to store the parent of each node + vector depth; // Depth array to store the depth of each node + vector> sparse_table; // Sparse table to store the LCA information + +public: + BinaryTree(int nodes) { + n = nodes; + parent.resize(n); + depth.resize(n); + sparse_table.resize(n, vector(log2(n) + 1)); + + // Initialize parent array with -1 + for (int i = 0; i < n; i++) { + parent[i] = -1; + } + } + + // Function to add an edge between two nodes + void AddEdge(int child, int par) { + parent[child] = par; + } + + // Function to initialize the sparse table + void InitSparseTable() { + for (int i = 0; i < n; i++) { + sparse_table[i][0] = parent[i]; + } + + for (int j = 1; (1 << j) < n; j++) { + for (int i = 0; i < n; i++) { + if (sparse_table[i][j - 1] != -1) { + sparse_table[i][j] = sparse_table[sparse_table[i][j - 1]][j - 1]; + } + } + } + } + + // Function to find the LCA of two nodes using the sparse table + int findLCA(int u, int v) { + if (depth[u] < depth[v]) { + swap(u, v); + } + + int maxDepth = log2(depth[u]) + 1; + + for (int i = maxDepth - 1; i >= 0; i--) { + if (depth[u] - (1 << i) >= depth[v]) { + u = sparse_table[u][i]; + } + } + + if (u == v) { + return u; + } + + for (int i = maxDepth - 1; i >= 0; i--) { + if (sparse_table[u][i] != -1 && sparse_table[u][i] != sparse_table[v][i]) { + u = sparse_table[u][i]; + v = sparse_table[v][i]; + } + } + + return parent[u]; + } + + // Function to calculate the depth of each node + void CalculateDepths(int node, int d) { + depth[node] = d; + for (int child = 0; child < n; child++) + if (parent[child] == node) + CalculateDepths(child, d + 1); + } +}; diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617..da0ae92 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,19 @@ - +#include "lca.hpp" #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(LCA, Simple) { + BinaryTree tree(7); + tree.AddEdge(1, 0); + tree.AddEdge(2, 0); + tree.AddEdge(3, 1); + tree.AddEdge(4, 1); + tree.AddEdge(5, 4); + tree.AddEdge(6, 4); + + tree.CalculateDepths(0, 0); // Calculate the depth of each node + tree.InitSparseTable(); // Initialize the sparse table + + int u = 5; + int v = 6; + ASSERT_EQ(tree.findLCA(u, v), 4); // Stack [] }