Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 60 additions & 16 deletions Graph/Connectivity/HeirHolzer_algo_for_Euler_Cycle_or_Path.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,52 @@
**Condition for Euler path or cycle in directed graph is** -
* Indegree[u] == outdegree[u], for atleast n-2 vertices
* All vertices with non-zero indegree(or outdegree) should be part of same SCC (isolated vertices which doesnt contribute any edge maybe there)
* Let `in[u]=indegree(u)` and `out[u]=outdegree(u)`. Isolated vertices with `in[u]+out[u]==0` can be ignored.
* **Euler cycle (directed)** exists iff:
* `in[u]==out[u]` for all vertices `u`
* All vertices with `in[u]+out[u]>0` belong to the same SCC (strongly connected after ignoring isolated vertices)
* **Euler path (directed)** exists iff:
* Exactly one vertex `s` has `out[s]=in[s]+1` (start vertex)
* Exactly one vertex `t` has `in[t]=out[t]+1` (end vertex)
* For all other vertices `u`, `in[u]==out[u]`
* All vertices with `in[u]+out[u]>0` belong to the same weakly connected component (connected if directions are ignored)

**Condition for Euler path or cycle in undirected graph is** -
* All vertices should have even degree, atleast n-2 vertices.
* All vertices with non-zero degree should be connected ( isolated vertices which doesnt contribute any edge maybe there)
* Isolated vertices with `deg[u]==0` can be ignored.
* All vertices with non-zero degree should be connected.
* Following cases arise -
* If number of odd degree vertices is greater than 2 then no euler path or cycle
* If no odd degree vertices then there exist a Euler cycle.
* If odd degree vertices are 2 then there exist a Euler path.

**HeirHolzer algorithm for finding Euler path or cycle whichever exist** -
* Time Complexity = `O(m + nlogn)` for undirected graph, have to use vector of multiset to represent adjacency list
* Time Complexity = `O(m)` for directed graph
* Time Complexity = `O(m + n*n)` for adjacency matrix representation
* Following cases arise -
* Time Complexity = `O(m)` if adjacency list is stored as `vector<vector<int>>` (or list) with pointer/iterator per node (no log deletions)
* Time Complexity = `O(m log n)` if you use `vector<multiset<int>>` (because each erase/find is `log`)
* Time Complexity = `O(n*n)` for adjacency matrix representation (matrix scan based implementation)
* Undirected graph handling -
* If number of odd degree vertices is greater than 2 then no euler path or cycle
* If no odd degree vertices then there exist a Euler cycle.
* If odd degree vertices are 2 then there exist a Euler path. In this case **add an edge between odd vertices** and find euler cycle. See note below to further handle this case

**NOTE** - In result array which we obtain from finding euler cycle, `result[0] == result.back()`

**Code Description** -
* Push any node in stack
* Push a valid start node in stack:
* If odd degree vertices are 2, start from an odd vertex
* Else start from any vertex with non-zero degree (if all are zero then answer is trivial)
* While stack is not empty
* u = st.top();
* if `adj[u].size() == 0, push u to result array` and pop stack
* else:
* `v = adj[u].begin(); push v to stack`
* remove v from adj[u] and remove u from adj[v] (in case of undirected graph)
* Result array will contain the euler cycle, with `result[0] == result.back()`
* Result array will contain the euler cycle (often in reverse order of traversal), with `result[0] == result.back()` for a cycle

**NOTE** -
* If we added an edge to handle odd degree vertices then to print euler path do this -
* Let odd degree vertices be `v1` and `v2`
* Find index result array such that `result[i] == v1 && result[i+1] == v2 or vice versa`, let call that index `id`
* `print result from (id + 1 to result.size() - 1)`
* `print result from (1 to id)`
* Also ensure you started Hierholzer from a non-zero degree vertex (or odd vertex in path case), otherwise the algorithm may not traverse the component containing edges.

```c++
#include<bits/stdc++.h>
Expand Down Expand Up @@ -75,14 +89,32 @@ void solve(int test)
cout<<"No euler path or cycle"<<endl;
return;
}

if(m==0)
{
cout<<"Euler cycle found"<<endl;
cout<<0<<endl;
return;
}

if(odd == 2)
{
adj[v1].insert(v2);
adj[v2].insert(v1);
}

int start = -1;
if(odd==2) start = v1;
else
{
for(i=0;i<n;i++)
{
if(deg[i]>0){start=i;break;}
}
}

stack<int>s;
s.push(0);
s.push(start);

vector<int>res;
while(!s.empty())
Expand All @@ -98,7 +130,12 @@ void solve(int test)
{
int v = *adj[u].begin();
adj[u].erase(adj[u].begin());
auto it = adj[v].find(u); // first find one iterator pointing to u. Because simply calling multiset.erase(value) removes all instances of that value.
auto it = adj[v].find(u);
if(it==adj[v].end())
{
cout<<"Invalid edge state"<<endl;
return;
}
adj[v].erase(it);
s.push(v);
}
Expand All @@ -113,27 +150,34 @@ void solve(int test)
}
}

reverse(res.begin(),res.end());

if(odd==2)
{
vector<int>path;
int id = -1;
for(i=0;i<res.size()-1;i++)
for(i=0;i<(int)res.size()-1;i++)
{
if(res[i] == v1 && res[i+1] == v2){id=i;break;}
if(res[i] == v2 && res[i+1] == v1){id=i; break;}
}

for(i=id+1;i<res.size();i++)path.pb(res[i]);
for(i=1;i<=id;i++)path.pb(res[i]); // start from i = 1 because res[res.size()-1] == res[0] because it was a euler cycle ******
if(id==-1)
{
cout<<"Logic error: added edge not found"<<endl;
return;
}

for(i=id+1;i<(int)res.size()-1;i++)path.pb(res[i]);
for(i=0;i<=id;i++)path.pb(res[i]);

cout<<"Euler path found"<<endl;
for(auto v:path)cout<<v<<" ";
cout<<endl;
return;
}
cout<<"Euler cycle found"<<endl;

cout<<"Euler cycle found"<<endl;
for(auto v:res)cout<<v<<" ";
cout<<endl;

Expand Down