Skip to content

euler_walk.hpp

SECTIONGraph INCLUDEnoya/euler_walk.hpp

在图中构造恰好经过每条边一次的欧拉游走,并返回顶点与边顺序。

Complexity: Time: O(V + E). Space: O(V + E).

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include

/// @complexity Time: O(V + E).
/// Space: O(V + E).

#include <algorithm>
#include <cassert>
#include <optional>
#include <utility>
#include <vector>

namespace noya {

struct euler_walk_result {
  std::vector<int> vs;
  std::vector<int> eid;
};

namespace euler_walk_internal {

inline euler_walk_result
hierholzer(const std::vector<std::vector<std::pair<int, int>>> &g, int m,
           int s) {
  std::vector<int> ptr(g.size());
  std::vector<bool> vis(m);
  std::vector<std::pair<int, int>> stk = {{s, -1}};
  euler_walk_result rev;
  while (!stk.empty()) {
    int u = stk.back().first;
    while (ptr[u] < int(g[u].size()) && vis[g[u][ptr[u]].second]) {
      ptr[u]++;
    }
    if (ptr[u] == int(g[u].size())) {
      auto [u1, ie] = stk.back();
      stk.pop_back();
      rev.vs.push_back(u1);
      if (ie != -1) {
        rev.eid.push_back(ie);
      }
      continue;
    }
    auto [v, ei1] = g[u][ptr[u]++];
    if (!vis[ei1]) {
      vis[ei1] = true;
      stk.emplace_back(v, ei1);
    }
  }
  std::reverse(rev.vs.begin(), rev.vs.end());
  std::reverse(rev.eid.begin(), rev.eid.end());
  return rev;
}

} // namespace euler_walk_internal

/// @brief Find an Euler trail in an undirected multigraph, or nullopt if none
/// exists.
inline std::optional<euler_walk_result>
euler_walk_undirected(int n, const std::vector<std::pair<int, int>> &es,
                      int s = -1) {
  assert(n >= 0);
  if (n == 0) {
    return es.empty() && s == -1
               ? std::optional<euler_walk_result>(euler_walk_result{})
               : std::nullopt;
  }
  if (s < -1 || s >= n) {
    return std::nullopt;
  }
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> deg(n);
  for (int id = 0; id < int(es.size()); id++) {
    auto [u, v] = es[id];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, id);
    g[v].emplace_back(u, id);
    deg[u]++;
    deg[v]++;
  }
  std::vector<int> odd;
  for (int u = 0; u < n; u++) {
    if (deg[u] & 1) {
      odd.push_back(u);
    }
  }
  if (odd.size() != 0 && odd.size() != 2) {
    return std::nullopt;
  }
  if (es.empty()) {
    int sel = s == -1 ? 0 : s;
    return euler_walk_result{{sel}, {}};
  }
  if (s == -1) {
    s = odd.empty() ? int(std::find_if(deg.begin(), deg.end(),
                                       [](int val) { return val > 0; }) -
                          deg.begin())
                    : odd[0];
  } else if ((!odd.empty() && deg[s] % 2 == 0) || deg[s] == 0) {
    return std::nullopt;
  }
  euler_walk_result res = euler_walk_internal::hierholzer(g, int(es.size()), s);
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  return res;
}

/// @brief Find an Euler trail in a directed multigraph, or nullopt if none
/// exists.
inline std::optional<euler_walk_result>
euler_walk_directed(int n, const std::vector<std::pair<int, int>> &es,
                    int s = -1) {
  assert(n >= 0);
  if (n == 0) {
    return es.empty() && s == -1
               ? std::optional<euler_walk_result>(euler_walk_result{})
               : std::nullopt;
  }
  if (s < -1 || s >= n) {
    return std::nullopt;
  }
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> din(n), dou(n);
  for (int id = 0; id < int(es.size()); id++) {
    auto [u, v] = es[id];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, id);
    dou[u]++;
    din[v]++;
  }
  int st = -1;
  int en = -1;
  for (int u = 0; u < n; u++) {
    int dif = dou[u] - din[u];
    if (dif == 1 && st == -1) {
      st = u;
    } else if (dif == -1 && en == -1) {
      en = u;
    } else if (dif != 0) {
      return std::nullopt;
    }
  }
  if ((st == -1) != (en == -1)) {
    return std::nullopt;
  }
  if (es.empty()) {
    int sel = s == -1 ? 0 : s;
    return euler_walk_result{{sel}, {}};
  }
  if (s == -1) {
    s = st;
    if (s == -1) {
      s = int(std::find_if(dou.begin(), dou.end(),
                           [](int val) { return val > 0; }) -
              dou.begin());
    }
  } else if ((st != -1 && s != st) || dou[s] == 0) {
    return std::nullopt;
  }
  euler_walk_result res = euler_walk_internal::hierholzer(g, int(es.size()), s);
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  return res;
}

} // namespace noya
#ifndef NOYA_EULER_WALK_HPP
#define NOYA_EULER_WALK_HPP 1

/// @complexity Time: O(V + E).
/// Space: O(V + E).

#include <algorithm>
#include <cassert>
#include <optional>
#include <utility>
#include <vector>

namespace noya {

struct euler_walk_result {
  std::vector<int> vs;
  std::vector<int> eid;
};

namespace euler_walk_internal {

inline euler_walk_result
hierholzer(const std::vector<std::vector<std::pair<int, int>>> &g, int m,
           int s) {
  std::vector<int> ptr(g.size());
  std::vector<bool> vis(m);
  std::vector<std::pair<int, int>> stk = {{s, -1}};
  euler_walk_result rev;
  while (!stk.empty()) {
    int u = stk.back().first;
    while (ptr[u] < int(g[u].size()) && vis[g[u][ptr[u]].second]) {
      ptr[u]++;
    }
    if (ptr[u] == int(g[u].size())) {
      auto [u1, ie] = stk.back();
      stk.pop_back();
      rev.vs.push_back(u1);
      if (ie != -1) {
        rev.eid.push_back(ie);
      }
      continue;
    }
    auto [v, ei1] = g[u][ptr[u]++];
    if (!vis[ei1]) {
      vis[ei1] = true;
      stk.emplace_back(v, ei1);
    }
  }
  std::reverse(rev.vs.begin(), rev.vs.end());
  std::reverse(rev.eid.begin(), rev.eid.end());
  return rev;
}

} // namespace euler_walk_internal

/// @brief Find an Euler trail in an undirected multigraph, or nullopt if none
/// exists.
inline std::optional<euler_walk_result>
euler_walk_undirected(int n, const std::vector<std::pair<int, int>> &es,
                      int s = -1) {
  assert(n >= 0);
  if (n == 0) {
    return es.empty() && s == -1
               ? std::optional<euler_walk_result>(euler_walk_result{})
               : std::nullopt;
  }
  if (s < -1 || s >= n) {
    return std::nullopt;
  }
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> deg(n);
  for (int id = 0; id < int(es.size()); id++) {
    auto [u, v] = es[id];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, id);
    g[v].emplace_back(u, id);
    deg[u]++;
    deg[v]++;
  }
  std::vector<int> odd;
  for (int u = 0; u < n; u++) {
    if (deg[u] & 1) {
      odd.push_back(u);
    }
  }
  if (odd.size() != 0 && odd.size() != 2) {
    return std::nullopt;
  }
  if (es.empty()) {
    int sel = s == -1 ? 0 : s;
    return euler_walk_result{{sel}, {}};
  }
  if (s == -1) {
    s = odd.empty() ? int(std::find_if(deg.begin(), deg.end(),
                                       [](int val) { return val > 0; }) -
                          deg.begin())
                    : odd[0];
  } else if ((!odd.empty() && deg[s] % 2 == 0) || deg[s] == 0) {
    return std::nullopt;
  }
  euler_walk_result res = euler_walk_internal::hierholzer(g, int(es.size()), s);
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  return res;
}

/// @brief Find an Euler trail in a directed multigraph, or nullopt if none
/// exists.
inline std::optional<euler_walk_result>
euler_walk_directed(int n, const std::vector<std::pair<int, int>> &es,
                    int s = -1) {
  assert(n >= 0);
  if (n == 0) {
    return es.empty() && s == -1
               ? std::optional<euler_walk_result>(euler_walk_result{})
               : std::nullopt;
  }
  if (s < -1 || s >= n) {
    return std::nullopt;
  }
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> din(n), dou(n);
  for (int id = 0; id < int(es.size()); id++) {
    auto [u, v] = es[id];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, id);
    dou[u]++;
    din[v]++;
  }
  int st = -1;
  int en = -1;
  for (int u = 0; u < n; u++) {
    int dif = dou[u] - din[u];
    if (dif == 1 && st == -1) {
      st = u;
    } else if (dif == -1 && en == -1) {
      en = u;
    } else if (dif != 0) {
      return std::nullopt;
    }
  }
  if ((st == -1) != (en == -1)) {
    return std::nullopt;
  }
  if (es.empty()) {
    int sel = s == -1 ? 0 : s;
    return euler_walk_result{{sel}, {}};
  }
  if (s == -1) {
    s = st;
    if (s == -1) {
      s = int(std::find_if(dou.begin(), dou.end(),
                           [](int val) { return val > 0; }) -
              dou.begin());
    }
  } else if ((st != -1 && s != st) || dou[s] == 0) {
    return std::nullopt;
  }
  euler_walk_result res = euler_walk_internal::hierholzer(g, int(es.size()), s);
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  return res;
}

} // namespace noya

#endif // NOYA_EULER_WALK_HPP
#include <algorithm>
#include <cassert>
#include <optional>
#include <utility>
#include <vector>

/// @complexity Time: O(V + E).
/// Space: O(V + E).

namespace noya {

struct euler_walk_result {
  std::vector<int> vs;
  std::vector<int> eid;
};

namespace euler_walk_internal {

inline euler_walk_result
hierholzer(const std::vector<std::vector<std::pair<int, int>>> &g, int m,
           int s) {
  std::vector<int> ptr(g.size());
  std::vector<bool> vis(m);
  std::vector<std::pair<int, int>> stk = {{s, -1}};
  euler_walk_result rev;
  while (!stk.empty()) {
    int u = stk.back().first;
    while (ptr[u] < int(g[u].size()) && vis[g[u][ptr[u]].second]) {
      ptr[u]++;
    }
    if (ptr[u] == int(g[u].size())) {
      auto [u1, ie] = stk.back();
      stk.pop_back();
      rev.vs.push_back(u1);
      if (ie != -1) {
        rev.eid.push_back(ie);
      }
      continue;
    }
    auto [v, ei1] = g[u][ptr[u]++];
    if (!vis[ei1]) {
      vis[ei1] = true;
      stk.emplace_back(v, ei1);
    }
  }
  std::reverse(rev.vs.begin(), rev.vs.end());
  std::reverse(rev.eid.begin(), rev.eid.end());
  return rev;
}

} // namespace euler_walk_internal

/// @brief Find an Euler trail in an undirected multigraph, or nullopt if none
/// exists.
inline std::optional<euler_walk_result>
euler_walk_undirected(int n, const std::vector<std::pair<int, int>> &es,
                      int s = -1) {
  assert(n >= 0);
  if (n == 0) {
    return es.empty() && s == -1
               ? std::optional<euler_walk_result>(euler_walk_result{})
               : std::nullopt;
  }
  if (s < -1 || s >= n) {
    return std::nullopt;
  }
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> deg(n);
  for (int id = 0; id < int(es.size()); id++) {
    auto [u, v] = es[id];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, id);
    g[v].emplace_back(u, id);
    deg[u]++;
    deg[v]++;
  }
  std::vector<int> odd;
  for (int u = 0; u < n; u++) {
    if (deg[u] & 1) {
      odd.push_back(u);
    }
  }
  if (odd.size() != 0 && odd.size() != 2) {
    return std::nullopt;
  }
  if (es.empty()) {
    int sel = s == -1 ? 0 : s;
    return euler_walk_result{{sel}, {}};
  }
  if (s == -1) {
    s = odd.empty() ? int(std::find_if(deg.begin(), deg.end(),
                                       [](int val) { return val > 0; }) -
                          deg.begin())
                    : odd[0];
  } else if ((!odd.empty() && deg[s] % 2 == 0) || deg[s] == 0) {
    return std::nullopt;
  }
  euler_walk_result res = euler_walk_internal::hierholzer(g, int(es.size()), s);
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  return res;
}

/// @brief Find an Euler trail in a directed multigraph, or nullopt if none
/// exists.
inline std::optional<euler_walk_result>
euler_walk_directed(int n, const std::vector<std::pair<int, int>> &es,
                    int s = -1) {
  assert(n >= 0);
  if (n == 0) {
    return es.empty() && s == -1
               ? std::optional<euler_walk_result>(euler_walk_result{})
               : std::nullopt;
  }
  if (s < -1 || s >= n) {
    return std::nullopt;
  }
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> din(n), dou(n);
  for (int id = 0; id < int(es.size()); id++) {
    auto [u, v] = es[id];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, id);
    dou[u]++;
    din[v]++;
  }
  int st = -1;
  int en = -1;
  for (int u = 0; u < n; u++) {
    int dif = dou[u] - din[u];
    if (dif == 1 && st == -1) {
      st = u;
    } else if (dif == -1 && en == -1) {
      en = u;
    } else if (dif != 0) {
      return std::nullopt;
    }
  }
  if ((st == -1) != (en == -1)) {
    return std::nullopt;
  }
  if (es.empty()) {
    int sel = s == -1 ? 0 : s;
    return euler_walk_result{{sel}, {}};
  }
  if (s == -1) {
    s = st;
    if (s == -1) {
      s = int(std::find_if(dou.begin(), dou.end(),
                           [](int val) { return val > 0; }) -
              dou.begin());
    }
  } else if ((st != -1 && s != st) || dou[s] == 0) {
    return std::nullopt;
  }
  euler_walk_result res = euler_walk_internal::hierholzer(g, int(es.size()), s);
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  return res;
}

} // namespace noya