diff --git a/lib/src/graph.cpp b/lib/src/graph.cpp new file mode 100644 index 0000000..64729ab --- /dev/null +++ b/lib/src/graph.cpp @@ -0,0 +1,115 @@ +#include "graph.hpp" + +#include + +void WeightedGraph::CheckNegativeCycle(const vector& distances) { + for (size_t u = 0; u < vertices_number_; ++u) { + for (size_t v = 0; v < vertices_number_; ++v) { + if (table_[u][v] != INF && distances[u] != INF) { + if (distances[u] + table_[u][v] < distances[v]) { + throw std::logic_error("Graph contains a negative weight cycle"); + } + } + } + } +} + +bool WeightedGraph::RelaxEdges(vector& distances) { + bool updated = false; + for (size_t u = 0; u < vertices_number_; ++u) { + for (size_t v = 0; v < vertices_number_; ++v) { + if (table_[u][v] != INF && distances[u] != INF) { + if (distances[u] + table_[u][v] < distances[v]) { + distances[v] = distances[u] + table_[u][v]; + updated = true; + } + } + } + } + return updated; +} + +vector WeightedGraph::BellmanFord(size_t start) { + vector distances(vertices_number_, INF); + distances[start] = 0; + + for (size_t i = 0; i < vertices_number_ - 1; ++i) { + RelaxEdges(distances); + } + + CheckNegativeCycle(distances); + return distances; +} + +vector WeightedGraph::Dijkstra(size_t start) { + vector distances(vertices_number_, INF); + distances[start] = 0; + + std::priority_queue, vector>, std::greater<>> + pq; + pq.push({0, start}); + + while (!pq.empty()) { + const int u = pq.top().second; + const int dist_u = pq.top().first; + pq.pop(); + + if (dist_u > distances[u]) continue; + + for (size_t v = 0; v < vertices_number_; ++v) { + if (table_[u][v] != INF && distances[u] != INF) { + const int new_dist = distances[u] + table_[u][v]; + if (new_dist < distances[v]) { + distances[v] = new_dist; + pq.push({new_dist, v}); + } + } + } + } + return distances; +} + +vector> WeightedGraph::Johnson() { + vector h(vertices_number_ + 1, 0); + + table_.push_back(vector(vertices_number_, 0)); + try { + h = BellmanFord(vertices_number_); + } catch (const std::logic_error&) { + throw std::logic_error( + "Graph contains a negative weight cycle, Johnson's algorithm cannot " + "be applied"); + } + table_.pop_back(); + + for (size_t u = 0; u < vertices_number_; ++u) { + for (size_t v = 0; v < vertices_number_; ++v) { + if (table_[u][v] != INF) { + table_[u][v] += h[u] - h[v]; + } + } + } + + vector> distances(vertices_number_); + for (size_t u = 0; u < vertices_number_; ++u) { + distances[u] = Dijkstra(u); + for (size_t v = 0; v < vertices_number_; ++v) { + if (distances[u][v] != INF) { + distances[u][v] += h[v] - h[u]; + } + } + } + + return distances; +} + +WeightedGraph::WeightedGraph(vector>&& table) + : table_(std::move(table)), vertices_number_(table_.size()) { + if (table_.empty()) + throw std::logic_error( + "the graph was not created because the input table is empty"); + for (size_t i = 0; i < table_.size(); i++) + if (table_[i].size() != table_.size()) + throw std::logic_error( + "the graph was not created because the input table is incorrect"); +} \ No newline at end of file diff --git a/lib/src/graph.hpp b/lib/src/graph.hpp new file mode 100644 index 0000000..96ecff1 --- /dev/null +++ b/lib/src/graph.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include + +using std::list; +using std::pair; +using std::vector; + +const int INF = std::numeric_limits::max(); + +template +class Graph { + private: + size_t vertices_number_; + vector> adjacency_list_; + + public: + Graph(size_t number) { + if (number == 0) throw std::logic_error("number must be positive!"); + vertices_number_ = number; + adjacency_list_.resize(vertices_number_ + 1); + } + + void AddEdge(size_t first_verticle, size_t second_verticle) { + if (first_verticle < 1 || first_verticle > vertices_number_ || + second_verticle < 1 || second_verticle > vertices_number_) + throw std::logic_error("such node does not exist"); + if (first_verticle == second_verticle) + throw std::logic_error("graph must be acyclic!"); + adjacency_list_[first_verticle].push_back(second_verticle); + } + + void TopologySortStep(int current_vertice, vector& visited_vertices, + list& list) { + visited_vertices[current_vertice] = true; + for (const auto& neighbor : adjacency_list_[current_vertice]) { + if (!visited_vertices[neighbor]) { + TopologySortStep(neighbor, visited_vertices, list); + } + } + list.push_front(current_vertice); + } + + list TopologySort() { + list list; + vector visited_vertices(vertices_number_ + 1, false); + for (size_t i = 1; i <= vertices_number_; i++) { + if (!visited_vertices[i]) { + TopologySortStep(i, visited_vertices, list); + } + } + return list; + } +}; + +class WeightedGraph { + private: + vector> table_; + size_t vertices_number_; + + void CheckNegativeCycle(const vector& distances); + + bool RelaxEdges(vector& distances); + + vector BellmanFord(size_t start); + + public: + WeightedGraph(vector>&& table); + + vector Dijkstra(size_t start); + + vector> Johnson(); +}; + +template +class BridgeGraph { + private: + size_t vertices_number_; + list* adjacency_list_; + vector tin_, low_; + vector visited_; + vector> bridges_; + vector articulation_points_; + + void dfs(int v, int p, int& timer) { + visited_[v] = true; + tin_[v] = low_[v] = timer++; + int children = 0; + + for (int to : adjacency_list_[v]) { + if (to == p) continue; + if (visited_[to]) { + low_[v] = std::min(low_[v], tin_[to]); + } else { + dfs(to, v, timer); + low_[v] = std::min(low_[v], low_[to]); + + if (low_[to] > tin_[v]) bridges_.push_back({v, to}); + if (articulation_points_.empty()) { + if (low_[to] >= tin_[v] && p != -1) { + if (articulation_points_.empty() || + articulation_points_.back() != v) { + articulation_points_.push_back(v); + } + } + } + ++children; + } + } + + if (p == -1 && children > 1) articulation_points_.push_back(v); + } + + public: + BridgeGraph(size_t number) { + if (number <= 0) throw std::logic_error("number must be positive!"); + vertices_number_ = number; + adjacency_list_ = new list[vertices_number_ + 1]; + } + + void AddEdge(size_t first_verticle, size_t second_verticle) { + if (first_verticle < 0 || first_verticle > vertices_number_ || + second_verticle < 0 || second_verticle > vertices_number_) + throw std::logic_error("such node does not exist"); + if (first_verticle == second_verticle) + throw std::logic_error("graph must be acyclic!"); + adjacency_list_[first_verticle].push_back(second_verticle); + adjacency_list_[second_verticle].push_back(first_verticle); + } + + void FindBridgesAndArticulationPoints() { + int timer = 0; + tin_.assign(vertices_number_ + 1, -1); + low_.assign(vertices_number_ + 1, -1); + visited_.assign(vertices_number_ + 1, false); + bridges_.clear(); + articulation_points_.clear(); + + for (size_t i = 0; i <= vertices_number_; ++i) { + if (!visited_[i]) dfs(i, -1, timer); + } + } + + vector> GiveBridges() { return bridges_; } + vector GivePoints() { return articulation_points_; } +}; \ No newline at end of file diff --git a/lib/src/util.cpp b/lib/src/util.cpp index 81e15bd..bd3c48f 100644 --- a/lib/src/util.cpp +++ b/lib/src/util.cpp @@ -1 +1 @@ -#include "util.hpp" +#include "util.hpp" \ No newline at end of file diff --git a/task_01/src/main.cpp b/task_01/src/main.cpp deleted file mode 100644 index 76e8197..0000000 --- a/task_01/src/main.cpp +++ /dev/null @@ -1 +0,0 @@ -int main() { return 0; } diff --git a/task_01/src/test.cpp b/task_01/src/test.cpp index 87cef73..d6ad0f0 100644 --- a/task_01/src/test.cpp +++ b/task_01/src/test.cpp @@ -1,5 +1,46 @@ #include -TEST(Test, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include + +TEST(TopologySort, test1) { + Graph g(6); + EXPECT_THROW(g.AddEdge(5, 5), std::logic_error); +} + +TEST(TopologySort, test2) { + EXPECT_THROW(Graph g(-5), std::logic_error); + EXPECT_THROW(Graph g(0), std::logic_error); +} + +TEST(TopologySort, test3) { + Graph g(2); + EXPECT_THROW(g.AddEdge(5, 6), std::logic_error); +} + +TEST(TopologySort, test4) { + Graph g(5); + g.AddEdge(5, 2); + g.AddEdge(5, 1); + g.AddEdge(4, 1); + g.AddEdge(4, 5); + g.AddEdge(2, 3); + g.AddEdge(3, 1); + ASSERT_EQ(g.TopologySort(), std::list({4, 5, 2, 3, 1})); +} + +TEST(TopologySort, test5) { + Graph g(12); + g.AddEdge(1, 2); + g.AddEdge(2, 3); + g.AddEdge(3, 4); + g.AddEdge(4, 5); + g.AddEdge(5, 6); + g.AddEdge(6, 7); + g.AddEdge(7, 8); + g.AddEdge(7, 8); + g.AddEdge(7, 8); + g.AddEdge(7, 8); + + ASSERT_EQ(g.TopologySort(), + std::list({12, 11, 10, 9, 1, 2, 3, 4, 5, 6, 7, 8})); } \ No newline at end of file diff --git a/task_01/src/topology_sort.cpp b/task_01/src/topology_sort.cpp new file mode 100644 index 0000000..af44719 --- /dev/null +++ b/task_01/src/topology_sort.cpp @@ -0,0 +1,3 @@ +// nothing + +int main() { return 0; } \ No newline at end of file diff --git a/task_02/src/algo.cpp b/task_02/src/algo.cpp new file mode 100644 index 0000000..af44719 --- /dev/null +++ b/task_02/src/algo.cpp @@ -0,0 +1,3 @@ +// nothing + +int main() { return 0; } \ No newline at end of file diff --git a/task_02/src/main.cpp b/task_02/src/main.cpp deleted file mode 100644 index 0e4393b..0000000 --- a/task_02/src/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int main() { return 0; } diff --git a/task_02/src/stack.cpp b/task_02/src/stack.cpp deleted file mode 100644 index 8ca8990..0000000 --- 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 deleted file mode 100644 index 138ec40..0000000 --- a/task_02/src/stack.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -class Stack { - public: - void Push(int value); - int Pop(); - - private: - std::stack data_; -}; - -class MinStack { - public: - void Push(int value); - int Pop(); - int GetMin(); - - private: - std::vector data_; -}; diff --git a/task_02/src/test.cpp b/task_02/src/test.cpp index 54e7ce9..1a96fff 100644 --- a/task_02/src/test.cpp +++ b/task_02/src/test.cpp @@ -1,42 +1,73 @@ #include -#include - -#include "stack.hpp" - -TEST(StackTest, Simple) { - Stack stack; - stack.Push(1); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] - stack.Push(1); // Stack [1] - stack.Push(2); // Stack [1, 2] - 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.Pop(), 2); // Stack [1] - stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.Pop(), 3); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] +#include + +using std::pair; + +TEST(BridgeGraphTest, test1) { + BridgeGraph g(5); + + g.AddEdge(1, 2); + g.AddEdge(1, 3); + g.AddEdge(3, 4); + g.AddEdge(3, 5); + g.AddEdge(4, 5); + + g.FindBridgesAndArticulationPoints(); + + vector> bridges({{1, 2}, {1, 3}}); + ASSERT_EQ(g.GiveBridges(), bridges); + vector articulation_points({3, 1}); + ASSERT_EQ(g.GivePoints(), articulation_points); +} + +TEST(BridgeGraphTest, test2) { + BridgeGraph g(5); + + g.AddEdge(1, 5); + g.AddEdge(1, 2); + g.AddEdge(1, 3); + g.AddEdge(2, 4); + g.AddEdge(2, 5); + g.AddEdge(3, 5); + + g.FindBridgesAndArticulationPoints(); + + vector> bridges({{2, 4}}); + ASSERT_EQ(g.GiveBridges(), bridges); + vector articulation_points({2}); + ASSERT_EQ(g.GivePoints(), articulation_points); } -TEST(MinStackTest, Simple) { - MinStack stack; - stack.Push(1); // Stack [1] - ASSERT_EQ(stack.GetMin(), 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.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.Pop(), 2); // Stack [1] - stack.Push(3); // Stack [1, 3] - ASSERT_EQ(stack.GetMin(), 1); - ASSERT_EQ(stack.Pop(), 3); // Stack [1] - ASSERT_EQ(stack.Pop(), 1); // Stack [] -} \ No newline at end of file +TEST(BridgeGraphTest, test3) { + BridgeGraph g(8); + + g.AddEdge(1, 2); + g.AddEdge(1, 8); + g.AddEdge(2, 6); + g.AddEdge(2, 8); + g.AddEdge(3, 4); + g.AddEdge(3, 5); + g.AddEdge(3, 6); + g.AddEdge(6, 7); + g.AddEdge(7, 8); + + g.FindBridgesAndArticulationPoints(); + + vector> bridges({{3, 4}, {3, 5}, {6, 3}}); + ASSERT_EQ(g.GiveBridges(), bridges); + vector articulation_points({3, 6}); + ASSERT_EQ(g.GivePoints(), articulation_points); +} + +TEST(BridgeGraphTest, test4) { + EXPECT_THROW(BridgeGraph g(-5), std::logic_error); + EXPECT_THROW(BridgeGraph g(0), std::logic_error); +} + +TEST(BridgeGraphTest, test5) { + BridgeGraph g(2); + EXPECT_THROW(g.AddEdge(5, 6), std::logic_error); + EXPECT_THROW(g.AddEdge(5, -4), std::logic_error); +} diff --git a/task_03/src/algo.cpp b/task_03/src/algo.cpp new file mode 100644 index 0000000..af44719 --- /dev/null +++ b/task_03/src/algo.cpp @@ -0,0 +1,3 @@ +// nothing + +int main() { return 0; } \ No newline at end of file diff --git a/task_03/src/main.cpp b/task_03/src/main.cpp deleted file mode 100644 index 0e4393b..0000000 --- a/task_03/src/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int main() { return 0; } diff --git a/task_03/src/test.cpp b/task_03/src/test.cpp index ef5a86a..a16c98c 100644 --- a/task_03/src/test.cpp +++ b/task_03/src/test.cpp @@ -1,8 +1,108 @@ #include -#include "topology_sort.hpp" +#include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +TEST(JohnsonAlgorithmTest, PositiveWeights) { + vector> graph = {{0, 3, INF, INF, INF}, + {INF, 0, 1, INF, 10}, + {4, INF, 0, 2, INF}, + {INF, INF, INF, 0, 1}, + {INF, INF, INF, INF, 0}}; + vector> expected_distances = {{0, 3, 4, 6, 7}, + {5, 0, 1, 3, 4}, + {4, 7, 0, 2, 3}, + {INF, INF, INF, 0, 1}, + {INF, INF, INF, INF, 0}}; + + WeightedGraph wg(std::move(graph)); + ASSERT_EQ(wg.Johnson(), expected_distances); +} + +TEST(JohnsonAlgorithmTest, NegativeWeights) { + vector> graph = {{0, -1, 4, INF, INF}, + {INF, 0, 3, 2, 2}, + {INF, INF, 0, INF, INF}, + {INF, 1, 5, 0, INF}, + {INF, INF, INF, -3, 0}}; + vector> expected_distances = {{0, -1, 2, -2, 1}, + {INF, 0, 3, -1, 2}, + {INF, INF, 0, INF, INF}, + {INF, 1, 4, 0, 3}, + {INF, -2, 1, -3, 0}}; + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Johnson(), expected_distances); +} + +TEST(JohnsonAlgorithmTest, NegativeCycle) { + vector> graph = {{0, 1, INF, INF}, + {INF, 0, -1, INF}, + {INF, INF, 0, -1}, + {-1, INF, INF, 0}}; + + WeightedGraph wg(std::move(graph)); + EXPECT_THROW(wg.Johnson();, std::logic_error); +} + +TEST(JohnsonAlgorithmTest, PositiveWeightsLargerGraph) { + vector> graph = { + {0, 10, INF, INF, INF, 5}, {INF, 0, 1, INF, INF, 2}, + {INF, INF, 0, 4, INF, INF}, {7, INF, 6, 0, 3, INF}, + {INF, INF, 9, 2, 0, INF}, {INF, 3, INF, 9, 2, 0}}; + vector> expected_distances = { + {0, 8, 9, 13, 7, 5}, {INF, 0, 1, 5, 3, 2}, {INF, INF, 0, 4, 6, 7}, + {7, 10, 6, 0, 3, 9}, {9, 12, 8, 2, 0, 10}, {5, 3, 4, 8, 2, 0}}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Johnson(), expected_distances); +} + +TEST(JohnsonAlgorithmTest, PositiveWeightsDisconnectedGraph) { + vector> graph = { + {0, 3, INF, INF}, {INF, 0, 7, INF}, {INF, INF, 0, 2}, {INF, INF, INF, 0}}; + vector> expected_distances = { + {0, 3, 10, 12}, {INF, 0, 7, 9}, {INF, INF, 0, 2}, {INF, INF, INF, 0}}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Johnson(), expected_distances); } + +TEST(JohnsonAlgorithmTest, NegativeWeightsComplexGraph) { + vector> graph = { + {0, 4, INF, INF, INF, INF}, {INF, 0, -2, INF, INF, INF}, + {INF, INF, 0, 3, INF, INF}, {INF, INF, INF, 0, 2, 3}, + {INF, INF, INF, INF, 0, -1}, {INF, INF, INF, INF, INF, 0}}; + vector> expected_distances = { + {0, 4, 2, 5, 7, 6}, {INF, 0, -2, 1, 3, 2}, + {INF, INF, 0, 3, 5, 4}, {INF, INF, INF, 0, 2, 1}, + {INF, INF, INF, INF, 0, -1}, {INF, INF, INF, INF, INF, 0}}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Johnson(), expected_distances); +} + +TEST(JohnsonAlgorithmTest, NegativeAndPositiveWeightsMixed) { + vector> graph = {{0, INF, INF, 1}, + {INF, 0, -3, INF}, + {INF, INF, 0, INF}, + {10, INF, INF, 0}}; + vector> expected_distances = { + {0, 11, 8, 1}, {7, 0, -3, 8}, {INF, INF, 0, INF}, {10, 21, 18, 0}}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Johnson(), expected_distances); +} + +TEST(JohnsonAlgorithmTest, SingleVertex) { + vector> graph = {{0}}; + vector> expected_distances = {{0}}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Johnson(), expected_distances); +} + +TEST(JohnsonAlgorithmTest, EmptyGraph) { + vector> graph; + + EXPECT_THROW(WeightedGraph wg(std::move(graph)), std::logic_error); +} \ No newline at end of file diff --git a/task_03/src/topology_sort.cpp b/task_03/src/topology_sort.cpp deleted file mode 100644 index e53f670..0000000 --- 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 6f70f09..0000000 --- a/task_03/src/topology_sort.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/task_04/src/algo.cpp b/task_04/src/algo.cpp new file mode 100644 index 0000000..af44719 --- /dev/null +++ b/task_04/src/algo.cpp @@ -0,0 +1,3 @@ +// nothing + +int main() { return 0; } \ No newline at end of file diff --git a/task_04/src/main.cpp b/task_04/src/main.cpp deleted file mode 100644 index 0e4393b..0000000 --- a/task_04/src/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int main() { return 0; } diff --git a/task_04/src/test.cpp b/task_04/src/test.cpp index 5e11617..b197b8c 100644 --- a/task_04/src/test.cpp +++ b/task_04/src/test.cpp @@ -1,6 +1,89 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include + +TEST(DijkstraAlgorithmTest, PositiveWeights) { + vector> graph = {{0, 10, INF, 30, 100}, + {INF, 0, 50, INF, INF}, + {INF, INF, 0, INF, 10}, + {INF, INF, 20, 0, 60}, + {INF, INF, INF, INF, 0}}; + vector expected_distances = {0, 10, 50, 30, 60}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} + +TEST(DijkstraAlgorithmTest, DisconnectedGraph) { + vector> graph = {{0, INF, INF, INF, INF}, + {INF, 0, 15, INF, INF}, + {INF, INF, 0, INF, INF}, + {INF, INF, INF, 0, 5}, + {INF, INF, INF, INF, 0}}; + vector expected_distances = {0, INF, INF, INF, INF}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} + +TEST(DijkstraAlgorithmTest, SingleVertex) { + vector> graph = {{0}}; + vector expected_distances = {0}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} + +TEST(DijkstraAlgorithmTest, StartVertexDisconnected) { + vector> graph = {{0, INF, INF, INF}, + {INF, 0, 7, INF}, + {INF, INF, 0, 3}, + {INF, INF, INF, 0}}; + vector expected_distances = {0, INF, INF, INF}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); } + +TEST(DijkstraAlgorithmTest, MultiplePaths) { + vector> graph = {{0, 1, 4, INF, INF}, + {INF, 0, 4, 2, 7}, + {INF, INF, 0, 3, INF}, + {INF, INF, INF, 0, 1}, + {INF, INF, INF, INF, 0}}; + vector expected_distances = {0, 1, 4, 3, 4}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} + +TEST(DijkstraAlgorithmTest, GraphWithSelfLoops) { + vector> graph = { + {0, 10, INF, 5}, {INF, 0, 1, INF}, {INF, INF, 0, 4}, {7, INF, 2, 0}}; + vector expected_distances = {0, 10, 7, 5}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} + +TEST(DijkstraAlgorithmTest, LargerGraph) { + vector> graph = { + {0, 3, 1, INF, INF, INF}, {3, 0, 7, 5, 1, INF}, {1, 7, 0, 2, INF, 3}, + {INF, 5, 2, 0, 7, 4}, {INF, 1, INF, 7, 0, 6}, {INF, INF, 3, 4, 6, 0}}; + vector expected_distances = {0, 3, 1, 3, 4, 4}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} + +TEST(DijkstraAlgorithmTest, EvenLargerGraph) { + vector> graph = { + {0, 10, INF, INF, INF, 5, INF, INF}, {10, 0, 2, 1, INF, INF, INF, INF}, + {INF, 2, 0, 4, 8, INF, INF, INF}, {INF, 1, 4, 0, 2, INF, INF, INF}, + {INF, INF, 8, 2, 0, 3, 6, INF}, {5, INF, INF, INF, 3, 0, INF, 9}, + {INF, INF, INF, INF, 6, INF, 0, 1}, {INF, INF, INF, INF, INF, 9, 1, 0}}; + vector expected_distances = {0, 10, 12, 10, 8, 5, 14, 14}; + + WeightedGraph wg(std::move(graph)); + EXPECT_EQ(wg.Dijkstra(0), expected_distances); +} \ No newline at end of file diff --git a/task_05/src/main.cpp b/task_05/src/main.cpp deleted file mode 100644 index 0e4393b..0000000 --- a/task_05/src/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int main() { return 0; } diff --git a/task_05/src/rmq.cpp b/task_05/src/rmq.cpp new file mode 100644 index 0000000..af44719 --- /dev/null +++ b/task_05/src/rmq.cpp @@ -0,0 +1,3 @@ +// nothing + +int main() { return 0; } \ No newline at end of file diff --git a/task_05/src/rmq.hpp b/task_05/src/rmq.hpp new file mode 100644 index 0000000..348a703 --- /dev/null +++ b/task_05/src/rmq.hpp @@ -0,0 +1,68 @@ +#include +#include +#include + +using std::vector; + +/* +Один из самых быстрых реализаций алгоритма RMQ +Выдает запрос за O(1), предобработка структуры данных - O(n) +*/ + +template +struct RMQ { + vector data; + size_t size; + static const size_t block_size = 30; + vector masks; + vector table; + + int Operation(int x, int y) { return data[x] < data[y] ? x : y; } + int LeastSignificantBit(int x) { return x & -x; } + int MostSignificantBitIndex(int x) { return 31 - __builtin_clz(x); } + int Small(int r, int size = block_size) { + int dist_from_r = MostSignificantBitIndex(masks[r] & ((1 << size) - 1)); + return r - dist_from_r; + } + + RMQ(const vector& v) + : data(v), + size(data.size()), + masks(size), + table((static_cast(size / block_size) * 32)) { + int current_mask = 0; + for (size_t i = 0; i < size; i++) { + current_mask = (current_mask << 1) & ((1 << block_size) - 1); + while (current_mask > 0 && + Operation(i, i - MostSignificantBitIndex( + LeastSignificantBit(current_mask))) == i) { + current_mask ^= LeastSignificantBit(current_mask); + } + current_mask |= 1; + masks[i] = current_mask; + } + for (int i = 0; i < size / block_size; i++) + table[i] = Small(block_size * i + block_size - 1); + for (int j = 1; (1 << j) <= size / block_size; j++) + for (int i = 0; i + (1 << j) <= size / block_size; i++) + table[size / block_size * j + i] = + Operation(table[size / block_size * (j - 1) + i], + table[size / block_size * (j - 1) + i + (1 << (j - 1))]); + } + + T Query(size_t l, size_t r) { + if (l < 0 || r >= data.size()) + throw std::out_of_range("incorrect boundaries!"); + if (r - l + 1 <= block_size) return data[Small(r, r - l + 1)]; + int ans = Operation(Small(l + block_size - 1), Small(r)); + const size_t x = l / block_size + 1; + const size_t y = r / block_size - 1; + if (x <= y) { + const size_t j = MostSignificantBitIndex(y - x + 1); + ans = Operation( + ans, Operation(table[size / block_size * j + x], + table[size / block_size * j + y - (1 << j) + 1])); + } + return data[ans]; + } +}; diff --git a/task_05/src/test.cpp b/task_05/src/test.cpp index 5e11617..f8bbf4d 100644 --- a/task_05/src/test.cpp +++ b/task_05/src/test.cpp @@ -1,6 +1,56 @@ #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "rmq.hpp" + +TEST(RMQTest, test1) { + vector const data = {1, 3, 2, 7, 9, 11, 3, 2, 1, 5, + 6, 8, 4, 0, 3, 6, 7, 2, 5, 9, + 10, 12, 14, 13, 15, 18, 17, 16, 19, 20}; + RMQ rmq(data); + ASSERT_EQ(rmq.Query(0, 4), 1); + ASSERT_EQ(rmq.Query(5, 10), 1); + ASSERT_EQ(rmq.Query(10, 20), 0); + ASSERT_EQ(rmq.Query(0, 29), 0); + ASSERT_EQ(rmq.Query(15, 25), 2); +} + +TEST(RMQTest, test2) { + vector const data = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + RMQ rmq(data); + ASSERT_EQ(rmq.Query(0, 10), 0); + ASSERT_EQ(rmq.Query(5, 15), 0); + ASSERT_EQ(rmq.Query(10, 20), 0); + ASSERT_EQ(rmq.Query(0, 20), 0); + ASSERT_EQ(rmq.Query(3, 7), 3); +} + +TEST(RMQTest, test3) { + vector const data = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; + RMQ rmq(data); + ASSERT_EQ(rmq.Query(0, 10), 5); + ASSERT_EQ(rmq.Query(5, 15), 5); + ASSERT_EQ(rmq.Query(10, 20), 5); + ASSERT_EQ(rmq.Query(0, 20), 5); + ASSERT_EQ(rmq.Query(3, 7), 5); } + +TEST(RMQTest, test4) { + vector const data = {}; + RMQ rmq(data); + EXPECT_THROW(rmq.Query(1, 3), std::out_of_range); +} + +TEST(RMQTest, test5) { + vector const data = {1, 2, 3}; + RMQ rmq(data); + EXPECT_THROW(rmq.Query(0, 3), std::out_of_range); +} + +TEST(RMQTest, test6) { + vector const data = {1, 2, 3}; + RMQ rmq(data); + EXPECT_THROW(rmq.Query(-1, 2), std::out_of_range); +} \ No newline at end of file diff --git a/task_06/src/lca.cpp b/task_06/src/lca.cpp new file mode 100644 index 0000000..8fe7a8f --- /dev/null +++ b/task_06/src/lca.cpp @@ -0,0 +1,49 @@ +#include "lca.hpp" + +#include +#include + +LCA::LCA(const vector>& tree, int root) { + if (tree.empty()) throw std::logic_error("the input vector is empty!"); + for (const auto& i : tree) + if (i.empty()) throw std::logic_error("vertex vector is empty"); + n = tree.size(); + const int log_n = static_cast(log2(static_cast(n)) + 1); + up.assign(n, vector(log_n, -1)); + depth.resize(n); + Dfs(tree, root, root); +} + +int LCA::Query(int u, int v) { + if (u < 0 || v >= n) throw std::out_of_range("incorrect boundaries!"); + if (depth[u] < depth[v]) std::swap(u, v); + const size_t log_n = up[0].size(); + for (size_t i = log_n - 1; i >= 0; i--) { + if (depth[u] - (1 << i) >= depth[v]) u = up[u][i]; + } + if (u == v) return u; + for (size_t i = log_n - 1; i >= 0; i--) { + if (up[u][i] != up[v][i]) { + u = up[u][i]; + v = up[v][i]; + } + } + return up[u][0]; +} + +void LCA::Dfs(const vector>& tree, int v, int p) { + up[v][0] = p; + for (int i = 1; i < up[v].size(); i++) { + if (up[v][i - 1] != -1) { + up[v][i] = up[up[v][i - 1]][i - 1]; + } + } + for (const int u : tree[v]) { + if (u != p) { + depth[u] = depth[v] + 1; + Dfs(tree, u, v); + } + } +} + +int main() { return 0; } \ No newline at end of file diff --git a/task_06/src/lca.hpp b/task_06/src/lca.hpp new file mode 100644 index 0000000..723f971 --- /dev/null +++ b/task_06/src/lca.hpp @@ -0,0 +1,22 @@ +#include +#include + +using std::vector; + +/* +Алгоритм Фарака-Колтона и Бендера +Выдает запрос за O(1), предобработка структуры данных - O(n) +*/ + +class LCA { + public: + LCA(const vector>& tree, int root); + int Query(int u, int v); + + private: + vector> up; + vector depth; + size_t n; + + void Dfs(const vector>& tree, int v, int p); +}; diff --git a/task_06/src/main.cpp b/task_06/src/main.cpp deleted file mode 100644 index 0e4393b..0000000 --- a/task_06/src/main.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int main() { return 0; } diff --git a/task_06/src/test.cpp b/task_06/src/test.cpp index 5e11617..634fb40 100644 --- a/task_06/src/test.cpp +++ b/task_06/src/test.cpp @@ -1,6 +1,104 @@ - #include -TEST(TopologySort, Simple) { - ASSERT_EQ(1, 1); // Stack [] +#include "lca.hpp" + +TEST(LCATest, test1) { + vector> tree(10); + tree[0] = {1, 2}; + tree[1] = {0, 3, 4}; + tree[2] = {0, 5, 6}; + tree[3] = {1, 7}; + tree[4] = {1, 8, 9}; + tree[5] = {2}; + tree[6] = {2}; + tree[7] = {3}; + tree[8] = {4}; + tree[9] = {4}; + + LCA lca(tree, 0); + + ASSERT_EQ(lca.Query(3, 4), 1); + ASSERT_EQ(lca.Query(7, 8), 1); + ASSERT_EQ(lca.Query(5, 6), 2); + ASSERT_EQ(lca.Query(7, 9), 1); + ASSERT_EQ(lca.Query(8, 9), 4); } + +TEST(LCATest, test2) { + vector> tree(7); + tree[0] = {1, 2}; + tree[1] = {0, 3, 4}; + tree[2] = {0, 5, 6}; + tree[3] = {1}; + tree[4] = {1}; + tree[5] = {2}; + tree[6] = {2}; + + LCA lca(tree, 0); + + ASSERT_EQ(lca.Query(3, 4), 1); + ASSERT_EQ(lca.Query(3, 5), 0); + ASSERT_EQ(lca.Query(4, 6), 0); + ASSERT_EQ(lca.Query(5, 6), 2); + ASSERT_EQ(lca.Query(1, 2), 0); +} + +TEST(LCATest, test3) { + vector> tree(9); + tree[0] = {1, 2}; + tree[1] = {0, 3, 4}; + tree[2] = {0, 5, 6}; + tree[3] = {1, 7}; + tree[4] = {1, 8}; + tree[5] = {2}; + tree[6] = {2}; + tree[7] = {3}; + tree[8] = {4}; + + LCA lca(tree, 0); + + ASSERT_EQ(lca.Query(3, 4), 1); + ASSERT_EQ(lca.Query(7, 8), 1); + ASSERT_EQ(lca.Query(5, 6), 2); + ASSERT_EQ(lca.Query(7, 3), 3); + ASSERT_EQ(lca.Query(8, 4), 4); +} + +TEST(LCATest, test4) { + vector> tree(4); + tree[0] = {1, 2}; + tree[1] = {0, 3}; + tree[2] = {0}; + tree[3] = {1}; + + LCA lca(tree, 0); + + EXPECT_THROW(lca.Query(-1, 2), std::out_of_range); +} + +TEST(LCATest, test5) { + vector> tree(4); + tree[0] = {1, 2}; + tree[1] = {0, 3}; + tree[2] = {0}; + tree[3] = {1}; + + LCA lca(tree, 0); + + EXPECT_THROW(lca.Query(0, 4), std::out_of_range); +} + +TEST(LCATest, test6) { + vector> tree(4); + tree[0] = {1, 2}; + tree[1] = {0, 3}; + tree[2] = {0}; + + EXPECT_THROW(const LCA lca(tree, 0), std::logic_error); +} + +TEST(LCATest, test7) { + vector> const tree; + + EXPECT_THROW(const LCA lca(tree, 0), std::logic_error); +} \ No newline at end of file