I have written BFS and DFS code purely with my understanding as below. Which is along with the test example. Looking for the input - how I can make it better in terms of logic & data Structure. I know there are already cooked & may be perfect code in the internet but I wish to attempt mine.
PS: Code is not perfect however I have tested on given example. Apology if you find it messy. Your comments will be welcomed.
package com.company.graph;
import java.util.*;
public class BrethFirstSearch {
public static void main(String...args){
/*
Input Undirected Graph -
2 4 1
A-------B-------C-------D
| \ | \ |
7 | \9 13| 3\ |6
| \ | \ |
E----F----------G-------H
1 8 13
Task 1 - Represent this graph as Data Structure that have optimal Space & Time complexity for store & traversal
Task 2 - Perform "Breadth First Search"
Simplified Representation of Graph where replaced vertex names with numbers..
2 4 1
0-------1-------2-------3
| \ | \ |
7 | \9 13| 3\ |6
| \ | \ |
4----5----------6-------7
1 8 13
*/
// We store number instated of letters since in real world every vertex may have full qualified name ex - "LasVegas" instead of just "A"
Map<Integer,String> vertices = new HashMap<>();
vertices.put(0,"A");
vertices.put(1,"B");
vertices.put(2,"C");
vertices.put(3,"D");
vertices.put(4,"E");
vertices.put(5,"F");
vertices.put(6,"G");
vertices.put(7,"H");
Map<Edge, Integer> edges = new HashMap<>();
//Note - I have store both the side to make Graph search simpler. Comments will be welcomed!!
edges.put(new Edge(0,1), 2);
edges.put(new Edge(0,4), 7);
edges.put(new Edge(0,5), 9);
edges.put(new Edge(1,0), 2);
edges.put(new Edge(1,2), 4);
edges.put(new Edge(2,1), 4);
edges.put(new Edge(2,3), 1);
edges.put(new Edge(2,6), 13);
edges.put(new Edge(2,7), 3);
edges.put(new Edge(3,2), 1);
edges.put(new Edge(3,7), 6);
edges.put(new Edge(4,0), 7);
edges.put(new Edge(4,5), 1);
edges.put(new Edge(5,0), 9);
edges.put(new Edge(5,4), 1);
edges.put(new Edge(5,6), 8);
edges.put(new Edge(6,2), 13);
edges.put(new Edge(6,5), 8);
edges.put(new Edge(6,7), 13);
edges.put(new Edge(7,2), 3);
edges.put(new Edge(7,3), 6);
edges.put(new Edge(7,6), 13);
breadthFirstSearch(vertices, edges);
depthFirstSearch(vertices,edges);
}
static void depthFirstSearch(Map<Integer,String> vertices, Map<Edge, Integer> edges){
System.out.format("%n%n%n%n************ Depth First Search - DFS ***********%n%n");
LinkedList<Integer> listOfVertex = new LinkedList<>(vertices.keySet());
List<Edge> listOfEdges = new ArrayList<>(edges.keySet());
Stack<Integer> dfsStack = new Stack<>();
while (!listOfVertex.isEmpty()){
Integer v = listOfVertex.getFirst();
dfsStack.push(listOfVertex.remove());
System.out.format("*** Start DFS from Vertex %S ***%n", vertices.get(v));
while(!dfsStack.empty()){
Integer vO = v;
for (Edge edge: listOfEdges) {
if (v.equals(edge.getV1()) && listOfVertex.indexOf(edge.getV2()) != -1){ // found new vertex
Integer nextV = edge.getV2();
System.out.format(" Searching from Vertex %S -----> %S%n", vertices.get(edge.getV1()), vertices.get(nextV));
dfsStack.push(nextV);
listOfVertex.remove(nextV);
v = nextV;
break;
}
}
if(vO.equals(v)){ //means not new vertex found from current vertex
v = dfsStack.pop(); //Search for previous vertex
System.out.format("Vertex %S has been conquered %n", vertices.get(v));
}
}
}
}
static void breadthFirstSearch(Map<Integer,String> vertices, Map<Edge, Integer> edges){
System.out.format("************ Breadth First Search - BFS ***********%n%n");
LinkedList<Integer> listOfVertex = new LinkedList<>(vertices.keySet());
List<Edge> listOfEdges = new ArrayList<>(edges.keySet());
BfsQueue bfsQueue = new BfsQueue();
bfsQueue.add(listOfVertex.remove()); //start from 1st vertex = 0 alias A
while (!bfsQueue.isEmpty()){
//remove and start search from this vertex
Integer v = bfsQueue.remove();
System.out.format("Vertex %S has been conquered %n", vertices.get(v));
//Search the Vertices from v
listOfEdges.
forEach(edge -> {
if(v.equals(edge.getV1()) && listOfVertex.indexOf(edge.getV2()) != -1){
bfsQueue.add(edge.getV2());
}});
//Mark the Searched Vertex
Iterator<Integer> i = bfsQueue.getIterator();
while (i.hasNext()){
Integer vertex = i.next();
if (listOfVertex.remove(vertex)){
System.out.format(" Searching from Vertex %S ------> %S %n", vertices.get(v), vertices.get(vertex));
}
}
}
}
static class BfsQueue {
private LinkedList<Integer> list = new LinkedList<>();
Iterator<Integer> getIterator(){
return list.iterator();
}
Integer remove(){
Integer v = null;
if(!list.isEmpty()){
v = list.getFirst();
list.removeFirst();
}
return v;
}
void add(Integer v){
list.add(v);
}
boolean isEmpty(){
return list.isEmpty();
}
boolean isPresent(Integer v){
return list.indexOf(v) != -1;
}
}
static class Edge {
int v1; //1st vertex
int v2; //2nd vertex
public Edge(int v1, int v2) {
this.v1 = v1;
this.v2 = v2;
}
public int getV1() {
return v1;
}
public int getV2() {
return v2;
}
}
}
The Output is :
"C:\Program Files\Java\jdk-11.0.4\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3.4\lib\idea_rt.jar=64975:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3.4\bin" -Dfile.encoding=UTF-8 -classpath C:\CodeBase\Test\out\production\Test com.company.graph.BrethFirstSearch
************ Breadth First Search - BFS ***********
Vertex A has been conquered
Searching from Vertex A ------> E
Searching from Vertex A ------> B
Searching from Vertex A ------> F
Vertex E has been conquered
Vertex B has been conquered
Searching from Vertex B ------> C
Vertex F has been conquered
Searching from Vertex F ------> G
Vertex C has been conquered
Searching from Vertex C ------> H
Searching from Vertex C ------> D
Vertex G has been conquered
Vertex H has been conquered
Vertex D has been conquered
************ Depth First Search - DFS ***********
*** Start DFS from Vertex A ***
Searching from Vertex A -----> E
Searching from Vertex E -----> F
Searching from Vertex F -----> G
Searching from Vertex G -----> H
Searching from Vertex H -----> C
Searching from Vertex C -----> D
Vertex D has been conquered
Vertex C has been conquered
Searching from Vertex C -----> B
Vertex B has been conquered
Vertex H has been conquered
Vertex G has been conquered
Vertex F has been conquered
Vertex E has been conquered
Vertex A has been conquered
Process finished with exit code 0
One approach I tried is - Implemented edges as a Map where each entry has key as a vertex and value as a List containing edges i.e. all connecting vertices along with the weight. (I inspired through adjacency list approach over the internet and is more efficient than edge list as per my previous approach specially while searching connecting vertices from a
given vertex. The complexity of traversal could be n(n-1) i.e. O(n^2) in Edge List approach assuming all vertices connected to all vertices. Where as (n-1) i.e. O(n) in adjacency list approach as in n vertices graph n can be connected to all n-1 vertices)
i.e. if below is the graph -
2 4 1
0-------1-------2-------3
| \ | \ |
7 | \9 13| 3\ |6
| \ | \ |
4----5----------6-------7
1 8 13
then the map looks like -
Map<Integer, List<Edge>> verticesEdges = new HashMap<>();
verticesEdges.put(0, new LinkedList<>(List.of(new Edge(1,2), new Edge(4,7),new Edge(5,9) )));
verticesEdges.put(1, new LinkedList<>(List.of(new Edge(0,2),new Edge(2,4))) );
verticesEdges.put(2, new LinkedList<>(List.of(new Edge(1,4),new Edge(3,1),new Edge(6,13), new Edge(7,3))));
verticesEdges.put(3,new LinkedList<>(List.of( new Edge(2,1), new Edge(7,6))));
verticesEdges.put(4, new LinkedList<>(List.of(new Edge(0,7),new Edge(5,1) )));
verticesEdges.put(5, new LinkedList<>(List.of(new Edge(0,9), new Edge(4,1),new Edge(6,8) )));
verticesEdges.put(6, new LinkedList<>(List.of(new Edge(2,13), new Edge(5,8), new Edge(7,13))));
verticesEdges.put(7, new LinkedList<>(List.of(new Edge(2,3),new Edge(3,6),new Edge(6,13))));
Now the BFS and DFS code require minor modification. I have presented below just for verification perspective -
package com.company.graph;
import java.util.*;
public class GraphSearch {
public static void main(String...args){
/*
Input Undirected Graph -
2 4 1
A-------B-------C-------D
| \ | \ |
7 | \9 13| 3\ |6
| \ | \ |
E----F----------G-------H
1 8 13
Task 1 - Represent this graph as Data Structure that have optimal Space & Time complexity for store & traversal
Task 2 - Perform "Breadth First Search"
Simplified Representation of Graph where replaced vertex names with numbers..
2 4 1
0-------1-------2-------3
| \ | \ |
7 | \9 13| 3\ |6
| \ | \ |
4----5----------6-------7
1 8 13
*/
// We store number instated of letters since in real world every vertex may have full qualified name ex - "LasVegas" instead of just "A"
Map<Integer,String> vertices = new HashMap<>();
vertices.put(0,"A");
vertices.put(1,"B");
vertices.put(2,"C");
vertices.put(3,"D");
vertices.put(4,"E");
vertices.put(5,"F");
vertices.put(6,"G");
vertices.put(7,"H");
//Implemented edges as a Map where for each entry - key is a vertex and value is List containing edges i.e. all connecting vertices along with the weight !!
Map<Integer, List<Edge>> verticesEdges = new HashMap<>();
verticesEdges.put(0, new LinkedList<>(List.of(new Edge(1,2), new Edge(4,7),new Edge(5,9) )));
verticesEdges.put(1, new LinkedList<>(List.of(new Edge(0,2),new Edge(2,4))) );
verticesEdges.put(2, new LinkedList<>(List.of(new Edge(1,4),new Edge(3,1),new Edge(6,13), new Edge(7,3))));
verticesEdges.put(3,new LinkedList<>(List.of( new Edge(2,1), new Edge(7,6))));
verticesEdges.put(4, new LinkedList<>(List.of(new Edge(0,7),new Edge(5,1) )));
verticesEdges.put(5, new LinkedList<>(List.of(new Edge(0,9), new Edge(4,1),new Edge(6,8) )));
verticesEdges.put(6, new LinkedList<>(List.of(new Edge(2,13), new Edge(5,8), new Edge(7,13))));
verticesEdges.put(7, new LinkedList<>(List.of(new Edge(2,3),new Edge(3,6),new Edge(6,13))));
breadthFirstSearch(vertices, verticesEdges);
//depthFirstSearch(vertices,verticesEdges);
}
static void depthFirstSearch(Map<Integer,String> vertices, Map<Integer, List<Edge>> verticesEdges ){
System.out.format("%n%n%n%n************ Depth First Search - DFS ***********%n%n");
LinkedList<Integer> listOfVertex = new LinkedList<>(vertices.keySet());
Stack<Integer> dfsStack = new Stack<>();
while (!listOfVertex.isEmpty()){
Integer v = listOfVertex.getFirst();
dfsStack.push(listOfVertex.remove());
System.out.format("*** Start DFS from Vertex %S ***%n", vertices.get(v));
while(!dfsStack.empty()){
Integer vO = v;
for (Edge edge: verticesEdges.get(v)) {
if (listOfVertex.indexOf(edge.getV()) != -1){ // found new vertex
Integer nextV = edge.getV();
System.out.format(" Searching from Vertex %S -----> %S%n", vertices.get(v), vertices.get(nextV));
dfsStack.push(nextV);
listOfVertex.remove(nextV);
v = nextV;
break;
}
}
if(vO.equals(v)){ //means not new vertex found from current vertex
v = dfsStack.pop(); //Search for previous vertex
System.out.format("Vertex %S has been conquered %n", vertices.get(v));
}
}
}
}
static void breadthFirstSearch(Map<Integer,String> vertices, Map<Integer, List<Edge>> verticesEdges){
System.out.format("************ Breadth First Search - BFS ***********%n%n");
LinkedList<Integer> listOfVertex = new LinkedList<>(vertices.keySet());
BfsQueue bfsQueue = new BfsQueue();
bfsQueue.add(listOfVertex.remove()); //start from 1st vertex = 0 alias A
while (!bfsQueue.isEmpty()){
//remove and start search from this vertex
Integer v = bfsQueue.remove();
System.out.format("Vertex %S has been conquered %n", vertices.get(v));
//Search the Vertices from v
verticesEdges.get(v).forEach(edge -> {
if(listOfVertex.indexOf(edge.getV()) != -1){
bfsQueue.add(edge.getV());
}});
//Mark the Searched Vertex
Iterator<Integer> i = bfsQueue.getIterator();
while (i.hasNext()){
Integer vertex = i.next();
if (listOfVertex.remove(vertex)){
System.out.format(" Searching from Vertex %S ------> %S %n", vertices.get(v), vertices.get(vertex));
}
}
}
}
static class BfsQueue {
private LinkedList<Integer> list = new LinkedList<>();
Iterator<Integer> getIterator(){
return list.iterator();
}
Integer remove(){
Integer v = null;
if(!list.isEmpty()){
v = list.getFirst();
list.removeFirst();
}
return v;
}
void add(Integer v){
list.add(v);
}
boolean isEmpty(){
return list.isEmpty();
}
boolean isPresent(Integer v){
return list.indexOf(v) != -1;
}
}
static class Edge {
int v; //Connecting Vertex
int w; //weight Of edge
public Edge(int v, int w) {
this.v = v;
this.w = w;
}
public int getV() {
return v;
}
public int getW() {
return w;
}
}
}
When I have ran it -
"C:\Program Files\Java\jdk-11.0.4\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3.4\lib\idea_rt.jar=50287:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3.4\bin" -Dfile.encoding=UTF-8 -classpath C:\CodeBase\Test\out\production\Test com.company.graph.GraphSearch
************ Breadth First Search - BFS ***********
Vertex A has been conquered
Searching from Vertex A ------> B
Searching from Vertex A ------> E
Searching from Vertex A ------> F
Vertex B has been conquered
Searching from Vertex B ------> C
Vertex E has been conquered
Vertex F has been conquered
Searching from Vertex F ------> G
Vertex C has been conquered
Searching from Vertex C ------> D
Searching from Vertex C ------> H
Vertex G has been conquered
Vertex D has been conquered
Vertex H has been conquered
************ Depth First Search - DFS ***********
*** Start DFS from Vertex A ***
Searching from Vertex A -----> B
Searching from Vertex B -----> C
Searching from Vertex C -----> D
Searching from Vertex D -----> H
Searching from Vertex H -----> G
Searching from Vertex G -----> F
Searching from Vertex F -----> E
Vertex E has been conquered
Vertex F has been conquered
Vertex G has been conquered
Vertex H has been conquered
Vertex D has been conquered
Vertex C has been conquered
Vertex B has been conquered
Vertex A has been conquered
Process finished with exit code 0
I am interested in finding sets of vertices that are not ordered in a directed acyclic graph (in the sense of a topological order).
That is, for example: two vertices in non-connected subgraphs, or the pairs (B,C), (B,D) in cases such as :
The naive possibility I thought of was to enumerate all the topological sorts (in this case [ A, B, C, D ] and [ A, C, D, B ] & find all pairs whose order ends up being different in at least two sorts, but this would be pretty expensive computationally.
Are there other, faster possibilities for what I want to achieve ? I am using boost.graph.
Basically what you want is the pair of nodes (u,v) such that there is no path from u to v, and no path from v to u. You can find for each node, all nodes that are reachable from that node using DFS. Total Complexity O(n(n+m)).
Now all you have to do is for each pair check if neither of the 2 nodes are reachable by the other.
You can start with a simple topological sort. Boost's implementation conveniently returns a reverse ordered list of vertices.
You can iterate that list, marking each initial leaf node with a new branch id until a shared node is encountered.
Demo Time
Let's start with the simplests of graph models:
#include <boost/graph/adjacency_list.hpp>
using Graph = boost::adjacency_list<>;
We wish to map branches:
using BranchID = int;
using BranchMap = std::vector<BranchID>; // maps vertex id -> branch id
We want to build, map and visualize the mappings:
Graph build();
BranchMap map_branches(Graph const&);
void visualize(Graph const&, BranchMap const& branch_map);
int main() {
// sample data
Graph g = build();
// do the topo sort and distinguish branches
BranchMap mappings = map_branches(g);
// output
visualize(g, mappings);
}
Building Graph
Just the sample data from the question:
Graph build() {
Graph g(4);
enum {A,B,C,D};
add_edge(A, B, g);
add_edge(A, C, g);
add_edge(C, D, g);
return g;
}
Mapping The Branches
As described in the introduction:
#include <boost/graph/topological_sort.hpp>
std::vector<BranchID> map_branches(Graph const& g) {
std::vector<Vertex> reverse_topo;
boost::topological_sort(g, back_inserter(reverse_topo));
// traverse the output to map to unique branch ids
std::vector<BranchID> branch_map(num_vertices(g));
BranchID branch_id = 0;
for (auto v : reverse_topo) {
auto degree = out_degree(v, g);
if (0 == degree) // is leaf?
++branch_id;
if (degree < 2) // "unique" path
branch_map[v] = branch_id;
}
return branch_map;
}
Visualizing
Let's write a graph-viz representation with each branch colored:
#include <boost/graph/graphviz.hpp>
#include <iostream>
void visualize(Graph const& g, BranchMap const& branch_map) {
// display helpers
std::vector<std::string> const colors { "gray", "red", "green", "blue" };
auto name = [](Vertex v) -> char { return 'A'+v; };
auto color = [&](Vertex v) -> std::string { return colors[branch_map.at(v) % colors.size()]; };
// write graphviz:
boost::dynamic_properties dp;
dp.property("node_id", transform(name));
dp.property("color", transform(color));
write_graphviz_dp(std::cout, g, dp);
}
This uses a tiny shorthand helper to create the transforming property maps:
// convenience short-hand to write transformed property maps
template <typename F>
static auto transform(F f) { return boost::make_transform_value_property_map(f, boost::identity_property_map{}); };
To compile this on a non-c++14 compiler you can replace the call to transform with the expanded body
Full Listing
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
using Graph = boost::adjacency_list<>;
using BranchID = int;
using BranchMap = std::vector<BranchID>; // maps vertex id -> branch id
Graph build();
BranchMap map_branches(Graph const&);
void visualize(Graph const&, BranchMap const& branch_map);
int main() {
// sample data
Graph g = build();
// do the topo sort and distinguish branches
BranchMap mappings = map_branches(g);
// output
visualize(g, mappings);
}
using Vertex = Graph::vertex_descriptor;
Graph build() {
Graph g(4);
enum {A,B,C,D};
add_edge(A, B, g);
add_edge(A, C, g);
add_edge(C, D, g);
return g;
}
#include <boost/graph/topological_sort.hpp>
std::vector<BranchID> map_branches(Graph const& g) {
std::vector<Vertex> reverse_topo;
boost::topological_sort(g, back_inserter(reverse_topo));
// traverse the output to map to unique branch ids
std::vector<BranchID> branch_map(num_vertices(g));
BranchID branch_id = 0;
for (auto v : reverse_topo) {
auto degree = out_degree(v, g);
if (0 == degree) // is leaf?
++branch_id;
if (degree < 2) // "unique" path
branch_map[v] = branch_id;
}
return branch_map;
}
#include <boost/property_map/transform_value_property_map.hpp>
// convenience short-hand to write transformed property maps
template <typename F>
static auto transform(F f) { return boost::make_transform_value_property_map(f, boost::identity_property_map{}); };
#include <boost/graph/graphviz.hpp>
#include <iostream>
void visualize(Graph const& g, BranchMap const& branch_map) {
// display helpers
std::vector<std::string> const colors { "gray", "red", "green", "blue" };
auto name = [](Vertex v) -> char { return 'A'+v; };
auto color = [&](Vertex v) -> std::string { return colors[branch_map.at(v) % colors.size()]; };
// write graphviz:
boost::dynamic_properties dp;
dp.property("node_id", transform(name));
dp.property("color", transform(color));
write_graphviz_dp(std::cout, g, dp);
}
Printing
digraph G {
A [color=gray];
B [color=red];
C [color=green];
D [color=green];
A->B ;
A->C ;
C->D ;
}
And the rendered graph:
Summary
Nodes in branches with different colors cannot be compared.
Consider this DOT sample:
digraph Foo
{
subgraph clusterA
{
A -> B;
}
subgraph clusterB
{
X -> Y;
}
subgraph connection_type_1
{
edge [color=red];
A -> Y;
}
subgraph connection_type_1
{
edge [color=green];
B -> X;
}
subgraph node_type_1
{
node [style=filled, color=".5,.5,.5"]; // THIS LINE DOESN'T WORK
X [label="foo"];
A;
}
}
The structure is set out in the two clusters and the edges are added later in semantically/cosmetically equivalent groups. The edges are coloured as expected.
But this doesn't work with styling nodes. The marked line has no effect unless I move it into one of the cluster* subgraphs, but then it applies to all nodes within that subgraph.
What's odd is that label=foo works in the final subgraph, whereas style doesn't.
I have a feeling that the answer is going to be "you can only set node attributes the first time you mention them", but is there a way to say "the following nodes, wherever they are, should all have the following attributes"?
the line does not work, as it sets default attributes only and the nodes have been created already. the default attributes have no effect. the label is a concrete attribute overriding any default values and therefore takes effect.
So you should reorder the code to
create all nodes (with default attributes)
create all edges (again with default attributes)
assign the nodes to clusters
steps 2 and 3 would create the nodes with the defauls attributes then active. your example did work for the edges only by accident, as you did try it for non existing edges only. it would not have worked for the 2 already defined edges in the clusters.
digraph Foo {
subgraph node_type_1 {
node [style=filled, color=".5,.5,.5"];
A;
X [label="foo"];
}
subgraph node_type_2 {
node [style=none];
B;
Y;
}
subgraph connection_type_1 {
edge [color=red];
A -> Y;
A -> B;
}
subgraph connection_type_2 {
edge [color=green];
B -> X;
X -> Y;
}
subgraph clusterA {
A;
B;
}
subgraph clusterB {
X;
Y;
}
}
I want to render several trees simultaneously and place all root nodes and all leaf nodes on the same level.
Here's an example of what I'm trying to do. Root nodes A and X are on the same level, and so are leaf nodes B, D, and Z.
I unsuccessfully tried putting roots in one rank and leaves in another as follows:
digraph G {
rankdir = TB;
subgraph {
A -> B
A -> C
C -> D
X -> Y
rank = same; A; X;
rank = same; B; D; Y;
} /* closing subgraph */
}
And got this outcome where everything is on the same rank.
Any suggestions about what I should be trying? I've already got roots and leaves identified.
Putting the rank = same; ... statements in braces, e.g.:
digraph G {
rankdir = TB;
subgraph {
A -> B
A -> C
C -> D
X -> Y
// note that rank is used in the subgraph
{rank = same; A; X;}
{rank = same; B; D; Y;}
} /* closing subgraph */
}
... gives the desired result:
The ideal structure is actually rank max and rank min. No need for a subgraph or any other shenanigans. GraphViz has explicit facilities for this.
With complex graphs, rank=same will often end up near the middle of the graph. If you mean top and bottom, say top and bottom.
digraph G {
rankdir = TB;
A -> B;
A -> C -> D;
X -> Y;
{ rank=min; A; X; }
{ rank=max; B; D; Y; }
}
Here's a simple example inspired by #William John Holden's comment -
graph {
rankdir=LR;
a -- b -- c;
d -- e -- f;
b -- d; {rank = same; b; d;};
}