Skip to content

dominator_tree.hpp

SECTIONGraph INCLUDEnoya/dominator_tree.hpp

Immediate dominators in a directed graph using Lengauer-Tarjan. The root dominates itself; unreachable vertices have parent -1.

Verified by dominatortree.

求流程图中每个点的直接支配点;适合判断从源点到目标的所有路径必须经过哪些点。

Implementation

View on GitHub

#ifndef NOYA_DOMINATOR_TREE_HPP
#define NOYA_DOMINATOR_TREE_HPP 1

/// @complexity Time: O((V + E) log V) with the implemented link-eval structure.
/// Space: O(V + E).

#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>

namespace noya {

/// @brief Immediate dominators in a directed graph using Lengauer-Tarjan.
/// The root dominates itself; unreachable vertices have parent -1.
inline std::vector<int>
dominator_tree(const std::vector<std::vector<int>> &graph, int root) {
  const int n = int(graph.size());
  assert(0 <= root && root < n);

  std::vector<std::vector<int>> reverse_graph(n);
  for (int vertex = 0; vertex < n; vertex++) {
    for (int to : graph[vertex]) {
      assert(0 <= to && to < n);
      reverse_graph[to].push_back(vertex);
    }
  }

  std::vector<int> order(n, -1), parent(n, -1), next_edge(n);
  std::vector<int> vertices = {root};
  std::vector<int> stack = {root};
  order[root] = 0;
  while (!stack.empty()) {
    int vertex = stack.back();
    if (next_edge[vertex] == int(graph[vertex].size())) {
      stack.pop_back();
      continue;
    }
    int to = graph[vertex][next_edge[vertex]++];
    if (order[to] == -1) {
      parent[to] = vertex;
      order[to] = int(vertices.size());
      vertices.push_back(to);
      stack.push_back(to);
    }
  }

  std::vector<int> semidominator(n), label(n), ancestor(n, -1),
      candidate(n, -1);
  std::iota(semidominator.begin(), semidominator.end(), 0);
  std::iota(label.begin(), label.end(), 0);
  std::vector<std::vector<int>> bucket(n);
  std::vector<int> compression_path;
  compression_path.reserve(vertices.size());

  auto evaluate = [&](int start) {
    compression_path.clear();
    int vertex = start;
    while (ancestor[vertex] != -1) {
      compression_path.push_back(vertex);
      vertex = ancestor[vertex];
    }
    for (auto it = compression_path.rbegin(); it != compression_path.rend();
         ++it) {
      int current = *it;
      int above = ancestor[current];
      if (order[semidominator[label[above]]] <
          order[semidominator[label[current]]]) {
        label[current] = label[above];
      }
      ancestor[current] = vertex;
    }
    return label[start];
  };

  for (int index = int(vertices.size()) - 1; index >= 1; index--) {
    int vertex = vertices[index];
    for (int from : reverse_graph[vertex]) {
      if (order[from] == -1) {
        continue;
      }
      int best = evaluate(from);
      if (order[semidominator[best]] < order[semidominator[vertex]]) {
        semidominator[vertex] = semidominator[best];
      }
    }
    bucket[semidominator[vertex]].push_back(vertex);
    for (int pending : bucket[parent[vertex]]) {
      candidate[pending] = evaluate(pending);
    }
    bucket[parent[vertex]].clear();
    ancestor[vertex] = parent[vertex];
  }

  std::vector<int> immediate_dominator(n, -1);
  immediate_dominator[root] = root;
  for (int index = 1; index < int(vertices.size()); index++) {
    int vertex = vertices[index];
    immediate_dominator[vertex] =
        semidominator[vertex] == semidominator[candidate[vertex]]
            ? semidominator[vertex]
            : immediate_dominator[candidate[vertex]];
  }
  return immediate_dominator;
}

} // namespace noya

#endif // NOYA_DOMINATOR_TREE_HPP
#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>

/// @complexity Time: O((V + E) log V) with the implemented link-eval structure.
/// Space: O(V + E).

namespace noya {

/// @brief Immediate dominators in a directed graph using Lengauer-Tarjan.
/// The root dominates itself; unreachable vertices have parent -1.
inline std::vector<int>
dominator_tree(const std::vector<std::vector<int>> &graph, int root) {
  const int n = int(graph.size());
  assert(0 <= root && root < n);

  std::vector<std::vector<int>> reverse_graph(n);
  for (int vertex = 0; vertex < n; vertex++) {
    for (int to : graph[vertex]) {
      assert(0 <= to && to < n);
      reverse_graph[to].push_back(vertex);
    }
  }

  std::vector<int> order(n, -1), parent(n, -1), next_edge(n);
  std::vector<int> vertices = {root};
  std::vector<int> stack = {root};
  order[root] = 0;
  while (!stack.empty()) {
    int vertex = stack.back();
    if (next_edge[vertex] == int(graph[vertex].size())) {
      stack.pop_back();
      continue;
    }
    int to = graph[vertex][next_edge[vertex]++];
    if (order[to] == -1) {
      parent[to] = vertex;
      order[to] = int(vertices.size());
      vertices.push_back(to);
      stack.push_back(to);
    }
  }

  std::vector<int> semidominator(n), label(n), ancestor(n, -1),
      candidate(n, -1);
  std::iota(semidominator.begin(), semidominator.end(), 0);
  std::iota(label.begin(), label.end(), 0);
  std::vector<std::vector<int>> bucket(n);
  std::vector<int> compression_path;
  compression_path.reserve(vertices.size());

  auto evaluate = [&](int start) {
    compression_path.clear();
    int vertex = start;
    while (ancestor[vertex] != -1) {
      compression_path.push_back(vertex);
      vertex = ancestor[vertex];
    }
    for (auto it = compression_path.rbegin(); it != compression_path.rend();
         ++it) {
      int current = *it;
      int above = ancestor[current];
      if (order[semidominator[label[above]]] <
          order[semidominator[label[current]]]) {
        label[current] = label[above];
      }
      ancestor[current] = vertex;
    }
    return label[start];
  };

  for (int index = int(vertices.size()) - 1; index >= 1; index--) {
    int vertex = vertices[index];
    for (int from : reverse_graph[vertex]) {
      if (order[from] == -1) {
        continue;
      }
      int best = evaluate(from);
      if (order[semidominator[best]] < order[semidominator[vertex]]) {
        semidominator[vertex] = semidominator[best];
      }
    }
    bucket[semidominator[vertex]].push_back(vertex);
    for (int pending : bucket[parent[vertex]]) {
      candidate[pending] = evaluate(pending);
    }
    bucket[parent[vertex]].clear();
    ancestor[vertex] = parent[vertex];
  }

  std::vector<int> immediate_dominator(n, -1);
  immediate_dominator[root] = root;
  for (int index = 1; index < int(vertices.size()); index++) {
    int vertex = vertices[index];
    immediate_dominator[vertex] =
        semidominator[vertex] == semidominator[candidate[vertex]]
            ? semidominator[vertex]
            : immediate_dominator[candidate[vertex]];
  }
  return immediate_dominator;
}

} // namespace noya