Implementation
class Graph {
public:
Graph(const vector<pair<string, string>> &connections) {
// good metric for reservation, consider the worse case:
// A->B; C->D; D->E; E->F == size == 4, but has 8 graph members
g.reserve(connections.size() + connections.size() *.5);
process_connections(connections);
}
Graph(unordered_map<string, unordered_set<string>> &graph) : g(graph) {}
unordered_map<string, unordered_set<string>> g;
private:
// handles user defined redundent connections
void process_connections(const vector<pair<string, string>> &data) {
for (auto &p : data) {
if (g.find(p.first) == g.end())
g.insert({ p.first, unordered_set<string>() });
if (g.find(p.second) == g.end())
g.insert({ p.second, unordered_set<string>() });
}
for (auto &p : data) {
g[p.first].insert(p.second);
g[p.second].insert(p.first);
}
}
};
class EularianCycle {
public:
EularianCycle(Graph g) : g(g.g), restore(g.g) {
if (!isEularian(g)) {
cout << "Graph does not contain eularian cycle" << endl;
}
}
vector<string> Hierholzers_iterative(const string &start) {
vector<string> final_path;
if (!isEularian(this->g)) return final_path;
stack<string> temp_path;
temp_path.push(start);
while (!temp_path.empty()) {
string top = temp_path.top();
if (!g[top].empty()) {
string rand_elm = *g[top].begin();
temp_path.push(rand_elm);
// delete both
// A -> B implies B -> A
g[top].erase(rand_elm);
g[rand_elm].erase(top);
}
else {
final_path.push_back(top);
temp_path.pop();
}
}
g = restore;
return vector<string>(final_path.rbegin(), final_path.rend());
}
vector<string> Hierholzers_recursive(const string &start) {
vector<string> final_path;
if (!isEularian(this->g)) return final_path;
_Hierholzers_recursive(start, final_path);
g = restore;
return vector<string>(final_path.rbegin(), final_path.rend());
}
private:
using graph = unordered_map<string, unordered_set<string>>;
graph g, restore;
void _Hierholzers_recursive(const string ¤t, vector<string> &final_path) {
while (!g[current].empty()) {
string rand_elm = *g[current].begin();
g[current].erase(rand_elm);
g[rand_elm].erase(current);
_Hierholzers_recursive(rand_elm, final_path);
}
final_path.push_back(current);
}
// a graph has an eularian cycle iff every vertex has an even degree
static bool isEularian(const Graph &g) {
for (auto &adj : g.g) {
if (adj.second.size() % 2 != 0)
return false;
}
return true;
}
};
void test()
{
unordered_map<string, unordered_set<string>> graph =
{
{ "0",{ "1","3" } },
{ "1",{ "2","0","4","5" } },
{ "2",{ "5","4","3","1" } },
{ "3",{ "2","0" } },
{ "4",{ "2","1" } },
{ "5",{ "1","2" } }
};
Graph g1(graph);
EularianCycle ec1(g1);
print(ec1.Hierholzers_iterative("0"));
print(ec1.Hierholzers_recursive("0"));
graph =
{
{ "A",{ "B","D","C" } },
{ "B",{ "A","E","D","C" } },
{ "C",{ "B","A","D","E" } },
{ "D",{ "A","B","C" } },
{ "E",{ "B","C" } }
};
Graph g2(graph);
EularianCycle ec2(g2);
print(ec2.Hierholzers_iterative("A"));
print(ec2.Hierholzers_recursive("A"));
graph =
{
{ "A",{ "B","F" } },
{ "B",{ "A","E","D","C" } },
{ "C",{ "B","E","D","F" } },
{ "D",{ "B","E","F","C" } },
{ "E",{ "F","B","D","C" } },
{ "F",{ "A","E","D","C" } }
};
Graph g3(graph);
EularianCycle ec3(g3);
print(ec3.Hierholzers_iterative("A"));
print(ec3.Hierholzers_recursive("A"));
vector<pair<string, string>> pair_graph =
{ { "JFK","SFO" },{ "JFK","ATL" },{ "SFO","ATL" },{ "ATL","JFK" },{ "ATL","SFO" } };
Graph g4(pair_graph);
EularianCycle ec4(g4);
print(ec4.Hierholzers_iterative("JFK"));
print(ec4.Hierholzers_recursive("JFK"));
}
int main() {
test();
}
// Graph 1
0: 0
1: 1
2: 2
3: 5
4: 1
5: 4
6: 2
7: 3
8: 0
0: 0
1: 1
2: 2
3: 5
4: 1
5: 4
6: 2
7: 3
8: 0
// Graph 2
Graph does not contain eularian cycle
// Graph 3
0: A
1: B
2: E
3: F
4: D
5: B
6: C
7: E
8: D
9: C
10: F
11: A
0: A
1: B
2: E
3: F
4: D
5: B
6: C
7: E
8: D
9: C
10: F
11: A
// Graph 4 (not drawn)
0: JFK
1: SFO
2: ATL
3: JFK
0: JFK
1: SFO
2: ATL
3: JFK
Last updated
Was this helpful?