Can I use just a simple Set for cycle detection in directed graph? - algorithm

I have seen quite a lot of algorithms to do cycle detection, such as Tarjan's strongly connected components algorithm answered in Best algorithm for detecting cycles in a directed graph.
I never thought detecting a cycle would be that complicated and I always believed a simple Set can help solve the problem.
So in addition to the usual marker array for recording visited vertices, we can use an additional Set to record all vertices along a path from the source.
The key thing is to remember to remove a vertex from the set after all its next neighbours are done.
A trivial code is like this:
public boolean hasCycle(List<Integer>[] vs) {
boolean[] marker = new boolean[v.length];
Set<Integer> tracker = new HashSet<Integer>();
for(int v = 0;v < vs.length;v++)
if (explore(v, vs, marker, tracker)) return true;
return false;
}
private boolean explore(int v, List<Integer>[] vs, boolean[] marker, Set<Integer> tracker) {
if (tracker.contains(v)) return true;
else if (!marker[v]) {
marker[v] = true;
tracker.add(v); // add current vertex to tracker
for (int u:vs[v]) if (explore(v, vs, marker, tracker)) return true;
tracker.remove(v); // remove the vertex from tracker, as it is fully done.
}
else return false;
}
Is there any problem about this algorithm?
With help from sasha's answer, actually even the set is not necessary and just an array is enough.
public boolean hasCycle(List<Integer>[] vs) {
boolean[] marker = new boolean[v.length];
boolean[] onPath = new boolean[v.length];
for(int v = 0;v < vs.length;v++)
if (explore(v, vs, marker, onPath)) return true;
return false;
}
private boolean explore(int v, List<Integer>[] vs, boolean[] marker, boolean[] onPath) {
if (onPath[v]) return true;
else if (!marker[v]) {
marker[v] = true;
onPath[v] = true; // add current vertex to the path
for (int u:vs[v]) if (explore(v, vs, marker, onPath)) return true;
onPath[v] = false; // remove the vertex from the path, as it is fully done.
}
else return false;
}

I am not an expert in java but talking about C++ passing set etc in recursion is not time efficient. Also set takes O(log(n)) in inserting/deleting an element. Your logic looks correct to me. But you can do it more efficiently and easily by keeping two arrays parent[] and visited[]. Basically do a bfs and following is the pseudo code ( visited is initialized to all zeros ) .
/* There are n nodes from 0 to n-1 */
visited[0]=1
parent[0]=0
flag=0
queue.push(0)
while the queue is not empty
top = queue.front()
queue.pop()
for all neighbors x of top
if not visited[top]
visited[x]=1
parent[x]=top
queue.push(x)
else if visited[x] is 1 and parent[top] is not x
flag = 1
if flag is 1 cycle is there otherwise not
As it may not be necessary that starting from 0 all nodes are visited . So repeat until all nodes are visited. Complexity is O(E+V) slightly better than the complexity of your method O(E+VlogV). But it is simple to write and not recursive.

Related

DFS on disconnected Graphs

How does DFS(G,v) behaves for disconnected graphs ?
Suppose a graph has 3 connected components and DFS is applied on one of these 3 Connected components, then do we visit every component or just the on whose vertex DFS is applied.
Means Is it correct to say that
DFS on a graph having many components covers only 1 component.
I also tried online DFS visualization tools for disconnected graphs and they also support that it covers only 1 component. But still I want to confirm
https://www.cs.usfca.edu/~galles/visualization/DFS.html
https://visualgo.net/dfsbfs
Starting a search on a single component of a disconnected graph will search only that component; how could it be otherwise? There's no information to make a decision about when, how or where to move the search to a different component.
If you want to perform a complete search over a disconnected graph, you have two high level options:
Spin up a separate search of each component, then add some logic to make a choice among multiple results (if necessary). This has the advantage of easy partitioning logic for running searches in parallel.
Add logic to indicate how to combine the components into a "single" graph. Two ideas for this:
Add a dummy root node, and have the components be its children. This would be a neat workaround for tools which only cover one component, but you want to work with all three at once. It also means that if you're searching for a single best result, you're guaranteed to get the global best without any additional checks.
Put your components in a list and add logic that jumps to the next one when the search on the current completes. Add additional logic to compare multiple potential search results, if necessary.
Note that the same reasoning applies to breadth first searches as well.
I came up with a way that de DFS could search disconected parts of the graph, i don't know if is the best one but here is below.
function Node(id, val){
this.id = id;
this.val = val;
this.nodeChildren = {};
}
Node.prototype.addAssociation = function(node){
this.nodeChildren[node.val] = node;
}
function Graph(){
this.nodes = [];
this.countNodes = 0;
}
Graph.prototype.addNode = function(val){
var n = new Node(this.countNodes, val);
this.nodes.push(n);
this.countNodes++;
return n;
}
Graph.prototype.search = function(val){
var nodeIndex = 0;
var visited = {}; //Hashmap
var found = null;
//Loop within the nodes and check if we didn't found a result yet
while(nodeIndex < this.nodes.length && found ==null ){
if(nodeIndex == this.nodes.length) return null;
var currentNode = this.nodes[nodeIndex];
nodeIndex++;
console.log("searching from", currentNode.val, visited);
found = this.searchDFS(val, visited, currentNode);
}
return found;
}
Graph.prototype.searchDFS = function(val, visited, currentNode){
//Node already visited skip
if(visited[currentNode.id] ==1 ) {
console.log("already visited, skipping");
return null;
}
//Found the node return it
if(currentNode.val == val){
return currentNode;
}
visited[currentNode.id] = 1;
var keys = Object.keys(currentNode.nodeChildren);
for(var i=0; i<keys.length; i++){
var childNode = currentNode.nodeChildren[keys[i]];
if(visited != 1){
return this.searchDFS(val, visited, childNode);
}
}
}
var g = new Graph();
var n1 = g.addNode("a");
var n2 = g.addNode("b");
var n3 = g.addNode("c");
g.addNode("c1").addAssociation(n3);
g.addNode("c2").addAssociation(n2);
var n4 = g.addNode("d");
var n5 = g.addNode("e");
n1.addAssociation(n2);
n1.addAssociation(n3);
n2.addAssociation(n3);
n3.addAssociation(n4);
console.log("found", g.search("e"));

Detected Cycle in directed graph if the vertex is found in recursive stack-why?

I have read an article from here about how to detect cycle in a directed graph. The basic concept of this algorithm is if a node is found in recursive stack then there is a cycle, but i don't understand why. what is the logic here?
#include<iostream>
#include <list>
#include <limits.h>
using namespace std;
class Graph
{
int V; // No. of vertices
list<int> *adj; // Pointer to an array containing adjacency lists
bool isCyclicUtil(int v, bool visited[], bool *rs);
public:
Graph(int V); // Constructor
void addEdge(int v, int w); // to add an edge to graph
bool isCyclic(); // returns true if there is a cycle in this graph
};
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];
}
void Graph::addEdge(int v, int w)
{
adj[v].push_back(w); // Add w to v’s list.
}
bool Graph::isCyclicUtil(int v, bool visited[], bool *recStack)
{
if(visited[v] == false)
{
// Mark the current node as visited and part of recursion stack
visited[v] = true;
recStack[v] = true;
// Recur for all the vertices adjacent to this vertex
list<int>::iterator i;
for(i = adj[v].begin(); i != adj[v].end(); ++i)
{
if ( !visited[*i] && isCyclicUtil(*i, visited, recStack) )
return true;
else if (recStack[*i])
return true;
}
}
recStack[v] = false; // remove the vertex from recursion stack
return false;
}
bool Graph::isCyclic()
{
// Mark all the vertices as not visited and not part of recursion
// stack
bool *visited = new bool[V];
bool *recStack = new bool[V];
for(int i = 0; i < V; i++)
{
visited[i] = false;
recStack[i] = false;
}
for(int i = 0; i < V; i++)
if (isCyclicUtil(i, visited, recStack))
return true;
return false;
}
int main()
{
// Create a graph given in the above diagram
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
if(g.isCyclic())
cout << "Graph contains cycle";
else
cout << "Graph doesn't contain cycle";
return 0;
}
From a brief look, the code snippet is an implementation of depth-first search, which is a basic search technique for directed graphs; the same approach works for breadth-first search. Note that apparently this implementation works only if there is only one connected component, otherwise the test must be performed for each connected component until a cycle is found.
That being said, the technique works by choosing one node at will and starting a recursive search there. Basically, if the search discovers a node that is in the stack, there must be a cycle, since it has been previously reached.
In the current implementation, recStack is not actually the stack, it just indicates whether a specific node is currently in the stack, no sequence information is stored. The actual cycle is contained implicitly in the call stack. The cycle is the sequence of nodes for which the calls of isCyclicUtil has not yet returned. If the actual cycle has to be extracted, the implementation must be changed.
So essentailly, what this is saying, is if a node leads to itself, there is a cycle. This makes sense if you think about it!
Say we start at node1.
{node1 -> node2}
{node2 -> node3}
{node3 -> node4
node3 -> node1}
{node4 -> end}
{node1 -> node2}
{node2 -> node3}.....
This is a small graph that contains a cycle. As you can see, we traverse the graph, going from each node to the next. In some cases we reach and end, but even if we reach the end, our code wants to go back to the other branch off of node3 so that it can check it's next node. This node then leads back to node1.
This will happen forever if we let it, because the path starting at node1 leads back to itself. We are recursively putting each node we visit on the stack, and if we reach an end, we remove all of the nodes from the stack AFTER the branch. In our case, we would be removing node4 from the stack every time we hit the end, but the rest of the nodes would stay on the stack because of the branch off of node3.
Hope this helps!

Can someone tell me why this code uses DFS?

I know if we deal with graph DFS, we use stack to track the node visited. But the code below is also using DFS. I read it a few times but still cannot figure out where it is DFS.Thanks!
public ArrayList<ArrayList<Integer>> combinationSum(int[] candidates, int target) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> item = new ArrayList<Integer>();
if(candidates == null || candidates.length==0)
return res;
Arrays.sort(candidates);
helper(candidates,target, 0, item ,res);
return res;
}
private void helper(int[] candidates, int target, int start, ArrayList<Integer> item,
ArrayList<ArrayList<Integer>> res){
if(target<0)
return;
if(target==0){
res.add(new ArrayList<Integer>(item));
return;
}
for(int i=start;i<candidates.length;i++){
if(i>0 && candidates[i] == candidates[i-1])
item.add(candidates[i]);
int newtarget = target - candidates[i];
helper(candidates,newtarget,i,item,res);
item.remove(item.size()-1);
}
}
The DFS can be implemented using 2 basic techniques:
1. Using Stack
2. Using Recursion.
I know that you find it hard to believe that the code you provided is using DFS because you don't see an explicit stack anywhere. The Reason is because it has implemented DFS using the second technique.
And the second technique is very easy to implement too. The psuedocode for it is very simple.
DFS( vertex v )
{
mark v visited
for all unvisited vertices u adjacent to v
{
DFS(u)
}
}
The code you provided is using this simple technique only, but if you have studied the course Operating System, you will come to know that recursion uses the program stack to keep track of function calls, so you are indirectly still using stack, but that is a different issue.

SPOJ : Vertex Cover (PT07X) Wrong Answer

I am trying vertex cover problem. Even an imperfect code cleared all the cases on soj judge, but I got one test case (in comments) where it failed, so I tried to remove it. But, now its not accepting. Problem Link
Problem Description: You have to find the vertex cover of a wneighted, undirected tree i.e. to find a vertex set of minimum size in this tree such that each edge has as least one of its end-points in that set.
My Algorithm is based on DFS. Earlier I used a straightforward logic that, do DFS and while backtracking, if child vertex is not included, include its parent (if not already included). And, it got accepted. But, then it failed on a simple case of skewed tree with 6 vertex. The answer should be 2, but it was giving 3. So, I made slight modification.
I added another parameter to check if a vertex is already covered by its parent or its child, and if so, neglect. So, whenever a find a vertex not covered yet, I add it's parent in the vertex set.
My Old Source Code:
vector<int> edge[100000]; // to store edges
bool included[100000]; // to keep track of elements in vertex cover set
bool done[100000]; // to keep track of undiscivered nodes to do DFS on tree
int cnt; // count the elements in vertex set
/* Function performs DFS and makes a vertex cover set */
bool solve(int source){
done[source] = true;
for(unsigned int i = 0; i<edge[source].size(); ++i){
if(!done[edge[source][i]]){ // if node is undiscovered
if(!solve(edge[source][i]) && !included[source]){ // if child node is not included and neither its parent
included[source] = true; // element added to vertex cover set
cnt++; // increasing the size of set
}
}
}
return included[source]; // return the status of current source vertex
}
int main(){
int n,u,v;
scanint(n);
for(int i = 0; i<n-1; ++i){
done[i] = false;
included[i] = false;
scanint(u);
scanint(v);
edge[u-1].push_back(v-1);
edge[v-1].push_back(u-1);
}
done[n-1] = false;
included[n-1] = false;
cnt = 0;
solve(0);
printf("%d\n", cnt);
return 0;
}
My New Source Code:
vector<int> edge[100000]; // to store edges
bool incld[100000]; // to keep track of nodes in vertex cover set
bool covrd[100000]; // to keep track of nodes already covered
bool done[100000]; // to keep track of undiscovered nodes to perform DFS
int cnt; // keep track of size of vertex cover set
/* Function to calculate vertex cover set via DFS */
void solve(int source){
int child; // to store index of child node
done[source] = true;
for(unsigned int i = 0; i<edge[source].size(); ++i){
if(!done[edge[source][i]]){ // if child node is undiscovered
child = edge[source][i];
if(incld[child]) // if child node is included in vertex set
covrd[source] = true; // setting current node to be covered
else if(!covrd[child] && !incld[source]){ // if child node is not covered and current node is not included in vertex set
incld[source] = true; // including current node
covrd[child] = true; // covering child node
cnt++; // incrementing size of vertex cover set
}
}
}
}
int main(){
int n,u,v;
scanint(n);
for(int i = 0; i<n-1; ++i){
done[i] = false;
incld[i] = false;
covrd[i] = false;
scanint(u);
scanint(v);
edge[u-1].push_back(v-1);
edge[v-1].push_back(u-1);
}
done[n-1] = false;
incld[n-1] = false;
covrd[n-1] = false;
cnt = 0;
solve(0);
printf("%d\n", cnt);
return 0;
}
Please help.
Your first solution is correct(you can find a proof here). The answer for a skewed tree with 6 vertices is actually 3(the comment in the link which says that the answer is 2 is wrong).

Writing a program to check if a graph is bipartite

I need to write a program that check if a graph is bipartite.
I have read through wikipedia articles about graph coloring and bipartite graph. These two article suggest methods to test bipartiteness like BFS search, but I cannot write a program implementing these methods.
Why can't you? Your question makes it hard for someone to even write the program for you since you don't even mention a specific language...
The idea is to start by placing a random node into a FIFO queue (also here). Color it blue. Then repeat this while there are nodes still left in the queue: dequeue an element. Color its neighbors with a different color than the extracted element and insert (enqueue) each neighbour into the FIFO queue. For example, if you dequeue (extract) an element (node) colored red, color its neighbours blue. If you extract a blue node, color its neighbours red. If there are no coloring conflicts, the graph is bipartite. If you end up coloring a node with two different colors, than it's not bipartite.
Like #Moron said, what I described will only work for connected graphs. However, you can apply the same algorithm on each connected component to make it work for any graph.
http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/breadthSearch.htm
Please read this web page, using breadth first search to check when you find a node has been visited, check the current cycle is odd or even.
A graph is bipartite if and only if it does not contain an odd cycle.
The detailed implementation is as follows (C++ version):
struct NODE
{
int color;
vector<int> neigh_list;
};
bool checkAllNodesVisited(NODE *graph, int numNodes, int & index);
bool checkBigraph(NODE * graph, int numNodes)
{
int start = 0;
do
{
queue<int> Myqueue;
Myqueue.push(start);
graph[start].color = 0;
while(!Myqueue.empty())
{
int gid = Myqueue.front();
for(int i=0; i<graph[gid].neigh_list.size(); i++)
{
int neighid = graph[gid].neigh_list[i];
if(graph[neighid].color == -1)
{
graph[neighid].color = (graph[gid].color+1)%2; // assign to another group
Myqueue.push(neighid);
}
else
{
if(graph[neighid].color == graph[gid].color) // touble pair in the same group
return false;
}
}
Myqueue.pop();
}
} while (!checkAllNodesVisited(graph, numNodes, start)); // make sure all nodes visited
// to be able to handle several separated graphs, IMPORTANT!!!
return true;
}
bool checkAllNodesVisited(NODE *graph, int numNodes, int & index)
{
for (int i=0; i<numNodes; i++)
{
if (graph[i].color == -1)
{
index = i;
return false;
}
}
return true;
}

Resources