-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
interview_prep/algorithm/java17/src/main/java/hoa/can/code/classic/graph/Edge.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package hoa.can.code.classic.graph; | ||
|
||
public class Edge { | ||
public final int f; | ||
public final int t; | ||
|
||
public Edge(int f, int t) { | ||
this.f = f; | ||
this.t = t; | ||
} | ||
|
||
public Edge rev(){ | ||
return new Edge(t,f); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format("[%d --> %d]", f, t); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
interview_prep/algorithm/java17/src/main/java/hoa/can/code/classic/graph/Graph.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package hoa.can.code.classic.graph; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public abstract class Graph<V, E extends Edge> { | ||
private List<V> vertices = new ArrayList<>(); | ||
protected List<List<E>> edges = new ArrayList<>(); | ||
|
||
public Graph(){} | ||
|
||
public Graph(List<V> vertices){ | ||
this.vertices.addAll(vertices); | ||
vertices.forEach(whatever -> edges.add(new ArrayList<>())); | ||
} | ||
|
||
public int edgeCount(){ | ||
return edges.stream().mapToInt(List::size).sum(); | ||
} | ||
|
||
public int add(V vertex){ | ||
vertices.add(vertex); | ||
edges.add(new ArrayList<>()); | ||
return vertices.size()-1; | ||
} | ||
|
||
public List<V> neighbor(int idx){ | ||
return edges.get(idx).stream() | ||
.map(e -> vertices.get(e.t)) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public List<V> neighbor(V v){ | ||
return neighbor(vertices.indexOf(v)); | ||
} | ||
|
||
public int idx(V v){ | ||
return vertices.indexOf(v); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
StringBuilder sb = new StringBuilder(); | ||
for(int i=0; i<vertices.size(); i++){ | ||
sb.append(vertices.get(i)); | ||
sb.append(">"); | ||
sb.append(Arrays.toString(neighbor(i).toArray())); | ||
sb.append(System.lineSeparator()); | ||
} | ||
return sb.toString(); | ||
} | ||
} |
173 changes: 173 additions & 0 deletions
173
interview_prep/algorithm/java17/src/main/java/hoa/can/code/classic/graph/Search.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package hoa.can.code.classic.graph; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.PriorityQueue; | ||
import java.util.Queue; | ||
import java.util.Set; | ||
import java.util.Stack; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
import java.util.function.ToDoubleFunction; | ||
|
||
public class Search { | ||
public static <T extends Comparable<T>> boolean linearContains(List<T> list, T key) { | ||
for (T item : list) { | ||
if (item.compareTo(key) == 0) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
// assumes *list* is already sorted | ||
public static <T extends Comparable<T>> boolean binaryContains(List<T> list, T key) { | ||
int low = 0; | ||
int high = list.size() - 1; | ||
while (low <= high) { | ||
int middle = (low + high) / 2; | ||
int comparison = list.get(middle).compareTo(key); | ||
if (comparison < 0) { // middle codon is less than key | ||
low = middle + 1; | ||
} else if (comparison > 0) { // middle codon is greater than key | ||
high = middle - 1; | ||
} else { // middle codon is equal to key | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
public static class Node<T> implements Comparable<Node<T>> { | ||
final T state; | ||
Node<T> parent; | ||
double cost; | ||
double heuristic; | ||
|
||
// for dfs and bfs we won't use cost and heuristic | ||
Node(T state, Node<T> parent) { | ||
this.state = state; | ||
this.parent = parent; | ||
} | ||
|
||
// for astar we will use cost and heuristic | ||
Node(T state, Node<T> parent, double cost, double heuristic) { | ||
this.state = state; | ||
this.parent = parent; | ||
this.cost = cost; | ||
this.heuristic = heuristic; | ||
} | ||
|
||
@Override | ||
public int compareTo(Node<T> other) { | ||
Double mine = cost + heuristic; | ||
Double theirs = other.cost + other.heuristic; | ||
return mine.compareTo(theirs); | ||
} | ||
} | ||
|
||
public static <T> Node<T> dfs( | ||
T initial, | ||
Predicate<T> goalTest, | ||
Function<T, List<T>> successors) { | ||
// frontier is where we've yet to go | ||
Stack<Node<T>> frontier = new Stack<>(); | ||
frontier.push(new Node<>(initial, null)); | ||
// explored is where we've been | ||
Set<T> explored = new HashSet<>(); | ||
explored.add(initial); | ||
|
||
// keep going while there is more to explore | ||
while (!frontier.isEmpty()) { | ||
Node<T> currentNode = frontier.pop(); | ||
T currentState = currentNode.state; | ||
if (goalTest.test(currentState)) { | ||
return currentNode; | ||
} | ||
// check where we can go next and haven't explored | ||
for (T child : successors.apply(currentState)) { | ||
if (explored.contains(child)) { | ||
continue; // skip children we already explored | ||
} | ||
explored.add(child); | ||
frontier.push(new Node<>(child, currentNode)); | ||
} | ||
} | ||
return null; // went through everything and never found goal | ||
} | ||
|
||
public static <T> List<T> nodeToPath(Node<T> node) { | ||
List<T> path = new ArrayList<>(); | ||
path.add(node.state); | ||
// work backwards from end to front | ||
while (node.parent != null) { | ||
node = node.parent; | ||
path.add(0, node.state); // add to front | ||
} | ||
return path; | ||
} | ||
|
||
public static <T> Node<T> bfs( | ||
T initial, | ||
Predicate<T> goalTest, | ||
Function<T, List<T>> successors) { | ||
// frontier is where we've yet to go | ||
Queue<Node<T>> frontier = new LinkedList<>(); | ||
frontier.offer(new Node<>(initial, null)); | ||
// explored is where we've been | ||
Set<T> explored = new HashSet<>(); | ||
explored.add(initial); | ||
|
||
// keep going while there is more to explore | ||
while (!frontier.isEmpty()) { | ||
Node<T> currentNode = frontier.poll(); | ||
T currentState = currentNode.state; | ||
if (goalTest.test(currentState)) { | ||
return currentNode; | ||
} | ||
// check where we can go next and haven't explored | ||
for (T child : successors.apply(currentState)) { | ||
if (explored.contains(child)) { | ||
continue; // skip children we already explored | ||
} | ||
explored.add(child); | ||
frontier.offer(new Node<>(child, currentNode)); | ||
} | ||
} | ||
return null; // went through everything and never found goal | ||
} | ||
|
||
public static <T> Node<T> astar( | ||
T initial, | ||
Predicate<T> goalTest, | ||
Function<T, List<T>> successors, | ||
ToDoubleFunction<T> heuristic) { | ||
// frontier is where we've yet to go | ||
PriorityQueue<Node<T>> frontier = new PriorityQueue<>(); | ||
frontier.offer(new Node<>(initial, null, 0.0, heuristic.applyAsDouble(initial))); | ||
// explored is where we've been | ||
Map<T, Double> explored = new HashMap<>(); | ||
explored.put(initial, 0.0); | ||
while (!frontier.isEmpty()) { | ||
Node<T> currentNode = frontier.poll(); | ||
T currentState = currentNode.state; | ||
if (goalTest.test(currentState)) { | ||
return currentNode; | ||
} | ||
// check where we can go next and haven't explored | ||
for (T child : successors.apply(currentState)) { | ||
// 1 here assumes a grid, need a cost function for more sophisticated apps | ||
double newCost = currentNode.cost + 1; | ||
if (!explored.containsKey(child) || explored.get(child) > newCost) { | ||
explored.put(child, newCost); | ||
frontier.offer(new Node<>(child, currentNode, newCost, heuristic.applyAsDouble(child))); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...rview_prep/algorithm/java17/src/main/java/hoa/can/code/classic/graph/UndirectedGraph.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package hoa.can.code.classic.graph; | ||
|
||
import java.util.List; | ||
|
||
public class UndirectedGraph<V> extends Graph<V, Edge> { | ||
public UndirectedGraph(List<V> vertices) { | ||
super(vertices); | ||
} | ||
|
||
public void add(Edge edge) { | ||
Edge revEdge = edge.rev(); | ||
edges.get(edge.f).add(edge); | ||
edges.get(edge.t).add(revEdge); | ||
} | ||
|
||
public void add(int f, int t) { | ||
add(new Edge(f, t)); | ||
} | ||
|
||
public void add(V f, V t) { | ||
add( | ||
new Edge(idx(f), idx(t)) | ||
); | ||
} | ||
} |
Empty file removed
0
interview_prep/algorithm/java17/src/main/java/hoa/can/code/ez/DivisibleSumPair.java
Empty file.
86 changes: 86 additions & 0 deletions
86
interview_prep/algorithm/java17/src/test/java/hoa/can/code/UndirectedGraphTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package hoa.can.code; | ||
|
||
import hoa.can.code.classic.graph.Search; | ||
import hoa.can.code.classic.graph.UndirectedGraph; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.List; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
public class UndirectedGraphTest { | ||
@Test | ||
@DisplayName("UndirectedGraphTest") | ||
public void test1() { | ||
UndirectedGraph<String> g = cities(); | ||
Search.Node<String> bfsResult = Search.bfs( | ||
"Seattle", | ||
v -> v.equals("Chicago"), | ||
g::neighbor); | ||
List<String> path = Search.nodeToPath(bfsResult); | ||
assertEquals(List.of("Seattle","Chicago"), path); | ||
|
||
} | ||
|
||
@Test | ||
@DisplayName("UndirectedGraphTest2") | ||
public void test2() { | ||
UndirectedGraph<String> g = cities(); | ||
Search.Node<String> bfsResult = Search.bfs( | ||
"Seattle", | ||
v -> v.equals("Riverside"), | ||
g::neighbor); | ||
List<String> path = Search.nodeToPath(bfsResult); | ||
assertEquals("Seattle", path.get(0)); | ||
assertEquals("Riverside", path.get(path.size()-1)); | ||
|
||
} | ||
|
||
@Test | ||
@DisplayName("UndirectedGraphTest3") | ||
public void test3() { | ||
UndirectedGraph<String> g = cities(); | ||
Search.Node<String> bfsResult = Search.bfs( | ||
"Chicago", | ||
v -> v.equals("Seattle"), | ||
g::neighbor); | ||
List<String> path = Search.nodeToPath(bfsResult); | ||
assertEquals(List.of("Chicago","Seattle"), path); | ||
|
||
} | ||
|
||
private UndirectedGraph<String> cities(){ | ||
UndirectedGraph<String> cityGraph = new UndirectedGraph<>( | ||
List.of("Seattle", "San Francisco", "Los Angeles", "Riverside", "Phoenix", "Chicago", "Boston", | ||
"New York", "Atlanta", "Miami", "Dallas", "Houston", "Detroit", "Philadelphia", "Washington")); | ||
|
||
cityGraph.add("Seattle", "Chicago"); | ||
cityGraph.add("Seattle", "San Francisco"); | ||
cityGraph.add("San Francisco", "Riverside"); | ||
cityGraph.add("San Francisco", "Los Angeles"); | ||
cityGraph.add("Los Angeles", "Riverside"); | ||
cityGraph.add("Los Angeles", "Phoenix"); | ||
cityGraph.add("Riverside", "Phoenix"); | ||
cityGraph.add("Riverside", "Chicago"); | ||
cityGraph.add("Phoenix", "Dallas"); | ||
cityGraph.add("Phoenix", "Houston"); | ||
cityGraph.add("Dallas", "Chicago"); | ||
cityGraph.add("Dallas", "Atlanta"); | ||
cityGraph.add("Dallas", "Houston"); | ||
cityGraph.add("Houston", "Atlanta"); | ||
cityGraph.add("Houston", "Miami"); | ||
cityGraph.add("Atlanta", "Chicago"); | ||
cityGraph.add("Atlanta", "Washington"); | ||
cityGraph.add("Atlanta", "Miami"); | ||
cityGraph.add("Miami", "Washington"); | ||
cityGraph.add("Chicago", "Detroit"); | ||
cityGraph.add("Detroit", "Boston"); | ||
cityGraph.add("Detroit", "Washington"); | ||
cityGraph.add("Detroit", "New York"); | ||
cityGraph.add("Boston", "New York"); | ||
cityGraph.add("New York", "Philadelphia"); | ||
cityGraph.add("Philadelphia", "Washington"); | ||
return cityGraph; | ||
} | ||
} |