Skip to content

Commit

Permalink
classic
Browse files Browse the repository at this point in the history
  • Loading branch information
s50600822 committed Oct 1, 2023
1 parent c807024 commit 70c6c3e
Show file tree
Hide file tree
Showing 6 changed files with 358 additions and 0 deletions.
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);
}
}
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();
}
}
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;
}
}
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.
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;
}
}

0 comments on commit 70c6c3e

Please sign in to comment.