/** * * \file * * \brief Data structure for finding the lowest common ancestor * of two vertices in a rooted tree using binary lifting. * * \details * Algorithm: https://cp-algorithms.com/graph/lca_binary_lifting.html * * Complexity: * - Precomputation: \f$O(N \log N)\f$ where \f$N\f$ is the number of vertices * in the tree * - Query: \f$O(\log N)\f$ * - Space: \f$O(N \log N)\f$ * * Example: *
Tree: *
 *             _  3  _
 *          /     |     \
 *        1       6       4
 *      / |     /   \       \
 *    7   5   2       8       0
 *            |
 *            9
 * 
* *
lowest_common_ancestor(7, 4) = 3 *
lowest_common_ancestor(9, 6) = 6 *
lowest_common_ancestor(0, 0) = 0 *
lowest_common_ancestor(8, 2) = 6 * * The query is symmetrical, therefore * lowest_common_ancestor(x, y) = lowest_common_ancestor(y, x) */ #include #include #include #include #include /** * \namespace graph * \brief Graph algorithms */ namespace graph { /** * Class for representing a graph as an adjacency list. * Its vertices are indexed 0, 1, ..., N - 1. */ class Graph { public: /** * \brief Populate the adjacency list for each vertex in the graph. * Assumes that evey edge is a pair of valid vertex indices. * * @param N number of vertices in the graph * @param undirected_edges list of graph's undirected edges */ Graph(size_t N, const std::vector > &undirected_edges) { neighbors.resize(N); for (auto &edge : undirected_edges) { neighbors[edge.first].push_back(edge.second); neighbors[edge.second].push_back(edge.first); } } /** * Function to get the number of vertices in the graph * @return the number of vertices in the graph. */ int number_of_vertices() const { return neighbors.size(); } /** \brief for each vertex it stores a list indicies of its neighbors */ std::vector > neighbors; }; /** * Representation of a rooted tree. For every vertex its parent is * precalculated. */ class RootedTree : public Graph { public: /** * \brief Constructs the tree by calculating parent for every vertex. * Assumes a valid description of a tree is provided. * * @param undirected_edges list of graph's undirected edges * @param root_ index of the root vertex */ RootedTree(const std::vector > &undirected_edges, int root_) : Graph(undirected_edges.size() + 1, undirected_edges), root(root_) { populate_parents(); } /** * \brief Stores parent of every vertex and for root its own index. * The root is technically not its own parent, but it's very practical * for the lowest common ancestor algorithm. */ std::vector parent; /** \brief Stores the distance from the root. */ std::vector level; /** \brief Index of the root vertex. */ int root; protected: /** * \brief Calculate the parents for all the vertices in the tree. * Implements the breadth first search algorithm starting from the root * vertex searching the entire tree and labeling parents for all vertices. * @returns none */ void populate_parents() { // Initialize the vector with -1 which indicates the vertex // wasn't yet visited. parent = std::vector(number_of_vertices(), -1); level = std::vector(number_of_vertices()); parent[root] = root; level[root] = 0; std::queue queue_of_vertices; queue_of_vertices.push(root); while (!queue_of_vertices.empty()) { int vertex = queue_of_vertices.front(); queue_of_vertices.pop(); for (int neighbor : neighbors[vertex]) { // As long as the vertex was not yet visited. if (parent[neighbor] == -1) { parent[neighbor] = vertex; level[neighbor] = level[vertex] + 1; queue_of_vertices.push(neighbor); } } } } }; /** * A structure that holds a rooted tree and allow for effecient * queries of the lowest common ancestor of two given vertices in the tree. */ class LowestCommonAncestor { public: /** * \brief Stores the tree and precomputs "up lifts". * @param tree_ rooted tree. */ explicit LowestCommonAncestor(const RootedTree &tree_) : tree(tree_) { populate_up(); } /** * \brief Query the structure to find the lowest common ancestor. * Assumes that the provided numbers are valid indices of vertices. * Iterativelly modifies ("lifts") u an v until it finnds their lowest * common ancestor. * @param u index of one of the queried vertex * @param v index of the other queried vertex * @return index of the vertex which is the lowet common ancestor of u and v */ int lowest_common_ancestor(int u, int v) const { // Ensure u is the deeper (higher level) of the two vertices if (tree.level[v] > tree.level[u]) { std::swap(u, v); } // "Lift" u to the same level as v. int level_diff = tree.level[u] - tree.level[v]; for (int i = 0; (1 << i) <= level_diff; ++i) { if (level_diff & (1 << i)) { u = up[u][i]; } } assert(tree.level[u] == tree.level[v]); if (u == v) { return u; } // "Lift" u and v to their 2^i th ancestor if they are different for (int i = static_cast(up[u].size()) - 1; i >= 0; --i) { if (up[u][i] != up[v][i]) { u = up[u][i]; v = up[v][i]; } } // As we regressed u an v such that they cannot further be lifted so // that their ancestor would be different, the only logical // consequence is that their parent is the sought answer. assert(up[u][0] == up[v][0]); return up[u][0]; } /* \brief reference to the rooted tree this structure allows to query */ const RootedTree &tree; /** * \brief for every vertex stores a list of its ancestors by powers of two * For each vertex, the first element of the corresponding list contains * the index of its parent. The i-th element of the list is an index of * the (2^i)-th ancestor of the vertex. */ std::vector > up; protected: /** * Populate the "up" structure. See above. */ void populate_up() { up.resize(tree.number_of_vertices()); for (int vertex = 0; vertex < tree.number_of_vertices(); ++vertex) { up[vertex].push_back(tree.parent[vertex]); } for (int level = 0; (1 << level) < tree.number_of_vertices(); ++level) { for (int vertex = 0; vertex < tree.number_of_vertices(); ++vertex) { // up[vertex][level + 1] = 2^(level + 1) th ancestor of vertex = // = 2^level th ancestor of 2^level th ancestor of vertex = // = 2^level th ancestor of up[vertex][level] up[vertex].push_back(up[up[vertex][level]][level]); } } } }; } // namespace graph /** * Unit tests * @returns none */ static void tests() { /** * _ 3 _ * / | \ * 1 6 4 * / | / \ \ * 7 5 2 8 0 * | * 9 */ std::vector > edges = { {7, 1}, {1, 5}, {1, 3}, {3, 6}, {6, 2}, {2, 9}, {6, 8}, {4, 3}, {0, 4}}; graph::RootedTree t(edges, 3); graph::LowestCommonAncestor lca(t); assert(lca.lowest_common_ancestor(7, 4) == 3); assert(lca.lowest_common_ancestor(9, 6) == 6); assert(lca.lowest_common_ancestor(0, 0) == 0); assert(lca.lowest_common_ancestor(8, 2) == 6); } /** Main function */ int main() { tests(); return 0; }