Skip to content

Commit 732edd5

Browse files
authored
Merge pull request #17 from owenong1/branch-bfs
bfs
2 parents 79af08a + 3494f52 commit 732edd5

File tree

7 files changed

+323
-55
lines changed

7 files changed

+323
-55
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package src.algorithms.graphs;
2+
3+
import java.util.*;
4+
5+
import src.algorithms.graphs.util.BinaryTreeNode;
6+
import src.algorithms.graphs.util.GraphNode;
7+
8+
/**
9+
* Implementation of BFS
10+
*
11+
* Breadth-First search is a graph traversal algorithm that utilizes a queue (FIFO) data structure
12+
* It is useful in finding a shortest-hops solution in graphs
13+
* This method is also used to obtain level order traversals of trees.
14+
*
15+
* In general, BFS works as such:
16+
* - Start with a queue that contains the root node
17+
* - While the queue is not empty:
18+
* - Pop a vertex from the queue
19+
* - Push all neighbours to the queue if they have not been visited yet
20+
* - Update any variables as needed
21+
*
22+
* Time: O(V + E), where V is the number of vertices/nodes, and E is the number of edges in the graph
23+
* Explanation: Each vertex is popped in O(1) time from the stack exactly once, hence O(V)
24+
* For each edge, we must check in O(1) time if we have visited the adjacent vertex to know
25+
* whether to push it into the queue, hence O(E).
26+
* Note that if the graph is a forest, BFS will likely terminate earlier, as fewer vertices are traversed
27+
*
28+
* Space: O(V): We utilize a Hashset to store the vertices we have visited already. In the worst case we have a
29+
* connected graph where all vertices are traversed, and our Hashset stores all O(V) vertices.
30+
* Further, we use a queue to hold the vertices to be traversed. In the worst case, the root will have all
31+
* other vertices as neighbours, and our queue will contain O(V) vertices.
32+
*
33+
* ** Note: The above description assumes an adjacency list in order to consider neighbours in an efficient manner
34+
* If an adjacency matrix were used, it would cost O(V) to find neighbours for a single vertex, making our
35+
* average case time complexity O(V^2) for a connected graph
36+
*
37+
* The implementation demonstrates the use of BFS in finding the level-order (Root, Left, Right) traversal of a binary tree
38+
* The tree is represented using a custom BinaryTreeNode class
39+
*
40+
*/
41+
public class breadthFirstSearch {
42+
43+
// Prints level order traversal from left to right
44+
public static List<Integer> levelOrder(BinaryTreeNode root) {
45+
if (root == null) { return new ArrayList<>(); }
46+
List<Integer> traversal = new ArrayList<>();
47+
Queue<BinaryTreeNode> queue = new LinkedList<>();
48+
queue.add(root);
49+
50+
while (!queue.isEmpty()) {
51+
BinaryTreeNode curr = queue.remove();
52+
traversal.add((Integer) curr.getVal());
53+
if (curr.getLeft() != null) { queue.add(curr.getLeft()); }
54+
if (curr.getRight() != null) { queue.add(curr.getRight()); }
55+
}
56+
57+
return traversal;
58+
}
59+
60+
// Finds the number of friend hops needed to go from person A to person B
61+
// Uses GraphNode<String>, where a node holds a String of the persons name, and an edge represents a friendship
62+
public static int friendHops(GraphNode<String> personA, GraphNode<String> personB) {
63+
// Hashset to store the people we have seen already
64+
HashSet<GraphNode> checked = new HashSet<>();
65+
// Hashmap to remember how many hops were needed to get to a specific friend *
66+
HashMap<GraphNode, Integer> map = new HashMap<>();
67+
68+
Queue<GraphNode> queue = new LinkedList<>();
69+
queue.add(personA);
70+
// the number of hops to the person themselves is 0, so we map: personA -> 0
71+
map.put(personA, 0);
72+
int hops = 0;
73+
74+
while (!queue.isEmpty()) {
75+
// poll the queue to get the next person to consider
76+
GraphNode<String> currPerson = queue.remove();
77+
// add the person to the checked hashset so we don't consider them again
78+
checked.add(currPerson);
79+
// grab the number of hops from the hashmap and add 1
80+
hops = map.get(currPerson) + 1;
81+
82+
List<GraphNode> neighbours = currPerson.neighbours();
83+
for (GraphNode neighbour : neighbours) {
84+
if (neighbour == personB) { return hops; }
85+
if (!checked.contains(neighbour)) {
86+
queue.add(neighbour);
87+
map.put(neighbour, hops);
88+
}
89+
}
90+
}
91+
// Returns -1 if person not found
92+
return -1;
93+
94+
// * Note that we can actually use just the hashmap instead of both the hashmap and the hashset!
95+
// This is because the hashmap supports the map.containsKey() function.
96+
}
97+
98+
public static int friendHopsVisualize(GraphNode<String> personA, GraphNode<String> personB) {
99+
// Hashset to store the people we have seen already
100+
HashSet<GraphNode> checked = new HashSet<>();
101+
// Hashmap to remember how many hops were needed to get to a specific friend *
102+
HashMap<GraphNode, Integer> map = new HashMap<>();
103+
104+
Queue<GraphNode> queue = new LinkedList<>();
105+
queue.add(personA);
106+
// the number of hops to the person themselves is 0, so we map: personA -> 0
107+
map.put(personA, 0);
108+
int hops = 0;
109+
110+
while (!queue.isEmpty()) {
111+
System.out.println("Current queue: " + String.valueOf(queue) + ", current hops: " + hops);
112+
// poll the queue to get the next person to consider
113+
GraphNode<String> currPerson = queue.remove();
114+
// add the person to the checked hashset so we don't consider them again
115+
checked.add(currPerson);
116+
// grab the number of hops from the hashmap and add 1
117+
hops = map.get(currPerson) + 1;
118+
119+
System.out.println("Looking at friends of: " + currPerson.toString());
120+
List<GraphNode> neighbours = currPerson.neighbours();
121+
for (GraphNode neighbour : neighbours) {
122+
if (neighbour == personB) { return hops; }
123+
if (!checked.contains(neighbour)) {
124+
queue.add(neighbour);
125+
map.put(neighbour, hops);
126+
}
127+
}
128+
System.out.println("Current queue: " + String.valueOf(queue) + ", current hops: " + hops);
129+
}
130+
// Returns -1 if person not found
131+
return -1;
132+
}
133+
}

src/algorithms/graphs/depthFirstSearch.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package src.algorithms.graphs;
22

33
import java.util.*;
4-
import src.algorithms.graphs.util.TreeNode;
4+
import src.algorithms.graphs.util.BinaryTreeNode;
55

66
/**
77
* Implementation of DFS
88
*
9-
* Depth-First search is a graph traversal algorithm that utilizes a stack (FIFO) data structure
9+
* Depth-First search is a graph traversal algorithm that utilizes a stack (LIFO) data structure
1010
* It is useful in finding a shortest-path solution (IN TREES), or a single solution in graphs
1111
* This method is also used to obtain order-traversals of trees.
1212
*
@@ -22,28 +22,32 @@
2222
* For each edge, we must check in O(1) time if we have visited the adjacent vertex to know
2323
* whether to push it into the stack, hence O(E).
2424
* Note that if the graph is a forest, DFS will likely terminate earlier, as fewer vertices are traversed
25+
*
2526
* Space: O(V): We utilize a Hashset to store the vertices we have visited already. In the worst case we have a
26-
* connected graph where all vertices are traversed, and our Hashset stores O(V) vertices.
27+
* connected graph where all vertices are traversed, and our Hashset stores all O(V) vertices.
2728
* Further, we use a stack to hold the vertices to be traversed. In the worst case, the root will have all
2829
* other vertices as neighbours, and our stack will contain O(V) vertices.
30+
*
2931
* ** Note: The above description assumes an adjacency list in order to consider neighbours in an efficient manner
3032
* If an adjacency matrix were used, it would cost O(V) to find neighbours for a single vertex, making our
3133
* average case time complexity O(V^2) for a connected graph
3234
*
3335
* The implementation demonstrates the use of DFS in finding the pre-order (Root, Left, Right) traversal of a binary tree
34-
* The tree is represented using a custom TreeNode class
36+
* The tree is represented using a custom BinaryTreeNode class
37+
*
38+
* TODO: Add new examples, and algo for all orderings
3539
*/
3640

3741
public class depthFirstSearch {
3842

39-
public static List<Integer> preOrder(TreeNode root) {
43+
public static List<Integer> preOrder(BinaryTreeNode root) {
4044
if (root == null) { return new ArrayList<>(); }
4145
List<Integer> traversal = new ArrayList<>();
42-
Stack<TreeNode> stack = new Stack<>();
46+
Stack<BinaryTreeNode> stack = new Stack<>();
4347
stack.push(root);
4448

4549
while (!stack.empty()) {
46-
TreeNode curr = stack.pop();
50+
BinaryTreeNode curr = stack.pop();
4751
traversal.add(curr.getVal());
4852
if (curr.getRight() != null) { stack.push(curr.getRight()); }
4953
if (curr.getLeft() != null) { stack.push(curr.getLeft()); }
@@ -53,15 +57,15 @@ public static List<Integer> preOrder(TreeNode root) {
5357
}
5458

5559
// call this for visualization of process
56-
public static List<Integer> preOrderVisualize(TreeNode root) {
60+
public static List<Integer> preOrderVisualize(BinaryTreeNode root) {
5761
if (root == null) { return new ArrayList<>(); }
5862
List<Integer> traversal = new ArrayList<>();
59-
Stack<TreeNode> stack = new Stack<>();
63+
Stack<BinaryTreeNode> stack = new Stack<>();
6064
stack.push(root);
6165

6266
while (!stack.empty()) {
6367
System.out.println("Current stack: " + stack.toString() + ", Current traversal: " + traversal.toString());
64-
TreeNode curr = stack.pop();
68+
BinaryTreeNode curr = stack.pop();
6569
System.out.println("Popped Node: " + Integer.toString(curr.getVal()));
6670
traversal.add(curr.getVal());
6771
System.out.println("Current stack: " + stack.toString() + ", Current traversal: " + traversal.toString());
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package src.algorithms.graphs.util;
2+
3+
public class BinaryTreeNode {
4+
int val;
5+
BinaryTreeNode left = null;
6+
BinaryTreeNode right = null;
7+
8+
public BinaryTreeNode(int val) {
9+
this.val = val;
10+
}
11+
12+
public BinaryTreeNode(int val, BinaryTreeNode left, BinaryTreeNode right) {
13+
this.val = val;
14+
this.left = left;
15+
this.right = right;
16+
}
17+
18+
public int getVal() { return this.val; }
19+
public BinaryTreeNode getLeft() { return this.left; }
20+
public BinaryTreeNode getRight() { return this.right; }
21+
22+
public void setLeft(BinaryTreeNode left) { this.left = left; }
23+
public void setRight(BinaryTreeNode right) { this.right = right; }
24+
25+
@Override
26+
public boolean equals(Object other) {
27+
if (other == this) { return true; }
28+
if (!(other instanceof BinaryTreeNode)) { return false; }
29+
BinaryTreeNode node = (BinaryTreeNode) other;
30+
return this.val == node.val;
31+
}
32+
33+
@Override
34+
public int hashCode() { return this.val; }
35+
36+
@Override
37+
public String toString() { return String.valueOf(this.val); }
38+
39+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package src.algorithms.graphs.util;
2+
3+
import java.util.*;
4+
5+
public class GraphNode <T> {
6+
public T val;
7+
public List<GraphNode> neighbour;
8+
9+
public GraphNode(T val) {
10+
this.val = val;
11+
neighbour = new ArrayList<>();
12+
}
13+
14+
public GraphNode(T val, List<GraphNode> neighbours) {
15+
this.val = val;
16+
this.neighbour = neighbours;
17+
}
18+
19+
public static <T> void connect(GraphNode<T> a, GraphNode<T> b) {
20+
a.neighbour.add(b);
21+
b.neighbour.add(a);
22+
}
23+
24+
25+
public List<GraphNode> neighbours() { return this.neighbour; }
26+
27+
public T getVal() { return this.val; }
28+
29+
@Override
30+
public String toString() { return String.valueOf(this.val); }
31+
32+
@Override
33+
public boolean equals(Object other) {
34+
if (other == this) { return true; }
35+
if (!(other instanceof GraphNode)) { return false; }
36+
GraphNode node = (GraphNode) other;
37+
return this.val == node.val;
38+
}
39+
40+
@Override
41+
public int hashCode() {
42+
return val.hashCode();
43+
}
44+
}

src/algorithms/graphs/util/TreeNode.java

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)