diff --git a/graph-search/notes.md b/graph-search/notes.md index 03f649113f94bfbd9659c4915b6a2a6fb908dd92..76ec7a4496bfa8c40eed7fbff6eaa7f9fa5b13c9 100644 --- a/graph-search/notes.md +++ b/graph-search/notes.md @@ -103,31 +103,54 @@ How many moves does it take to reverse the string `123`? ## Best-First Search -* Worklist: … -* Priority: … +* Worklist: Priority Queue +* Priority: Distance traveled from the source plus the estimated distance to the destination * Guaranteed to find a path if one exists * Not guaranteed to find a good path (but often does anyway) * Can have good best- and average-case time efficiency - Worklist Backpointers - ------------ -------------- - (⊥, a, 0) + Worklist Backpointers + ------------------ -------------- + (⊥, a, 0) → 2 ✓ a → (⊥, a, 0) + (a, c, 7) → 9 ✓ c → (a, c, 7) + (c, e, 12) → 13 ✓ b → (c, b, 8) + (c, b, 8) → 9 ✓ e → (c, e, 12) + (b, e, 17) → 18 d → (e, d, 15) + (b, a, 12) → 14 ✓ + (e, d, 15) → 15 ✓ + (e, f, 14) → 16 Reversed Path ---- - … + d ← e ← c ← a ## A* -* Worklist: … -* Priority: … +* Worklist: Priority Queue +* Priority: Distance traveled from the source plus the estimated distance to the destination (estimated by an *admissible* heuristic) * Guaranteed to find a path with least weight * Can have good best- and average-case time efficiency ## Recursive Depth-First Search (DFS) -* Worklist: … +* Worklist: Program Stack Activation Frames Backpointers Returned ----------------------------------- ------------ ------------------ - edge = (⊥, a), incidence = … + edge = (⊥, a), incidence = (a, c) a → (⊥, a) [a, c, b, e, f, d] + edge = (a, c), incidence = (c, b) c → (a, c) [c, b, e, f, d] + edge = (c, b), incidence = (b, e) b → (c, b) [b, e, f, d] + edge = (b, a) ⊥ + edge = (b, e), incidence = (e, f) e → (b, e) [e, f, d] + edge = (e, f), incidence = (f, d) f → (e, f) [f, d] + edge = (f, d) d → (f, d) [d] + + Activation Frames Visited Returned + ----------------------------------- ------------ ------------------ + source = a, incidence = (a, c) a [a, c, b, e, f, d] + source = c, incidence = (c, b) c [c, b, e, f, d] + source = b, incidence = (b, e) b [b, e, f, d] + source = a ⊥ + source = e, incidence = (e, f) e [e, f, d] + source = f, incidence = (f, d) f [f, d] + source = d d [d] diff --git a/graph-search/src/features/search/search.js b/graph-search/src/features/search/search.js index 86a91cc8b90ef22a0df1eb259c5e2dc70643abb1..d53d46109f289424e735663245bb5d5216339200 100644 --- a/graph-search/src/features/search/search.js +++ b/graph-search/src/features/search/search.js @@ -92,13 +92,62 @@ export function dijkstras(graph, source, destination) { } function heuristic(vertex, destination) { - return 0; // TODO: stub + if ('acf'.includes(vertex)) { + return 2; + } + if ('be'.includes(vertex)) { + return 1; + } + return 0; } export function bestFirst(graph, source, destination) { - return undefined; // TODO: stub + const backpointers = new Map(); + const worklist = new PriorityQueue(); + worklist.insert( + new Edge(undefined, source, 0), + 0 + heuristic(source, destination), + ); + while (worklist.size > 0) { + const workitem = worklist.remove(); + if (backpointers.has(workitem.to) && + backpointers.get(workitem.to).distance <= workitem.distance) { + continue; + } + backpointers.set(workitem.to, workitem); + if (workitem.to === destination) { + const reversedPath = []; + for (let current = destination; + current !== undefined; + current = backpointers.get(current).from) { + reversedPath.push(current); + } + return reversedPath.reverse(); + } + for (const incidence of graph.getIncidences(workitem.to)) { + worklist.insert( + new Edge(workitem.to, incidence.destination, workitem.distance + incidence.weight), + workitem.distance + incidence.weight + + heuristic(incidence.destination, destination), + ); + } + } + return undefined; } -export function recursiveDFS(graph, source, destination) { - return undefined; // TODO: stub +export function recursiveDFS(graph, source, destination, visited = new Set()) { + if (visited.has(source)) { + return undefined; + } + visited.add(source); + if (source === destination) { + return [source]; + } + for (const incidence of [...graph.getIncidences(source)].reverse()) { + const suffix = recursiveDFS(graph, incidence.destination, destination, visited); + if (suffix !== undefined) { + return [source, ...suffix]; + } + } + return undefined; } diff --git a/graph-search/src/features/search/solution.js b/graph-search/src/features/search/solution.js index a64ab4729a95691560c0aae8d3e2fe73cbf91fb3..94ab48c52e1bb295c71130825092d5b6c81942c1 100644 --- a/graph-search/src/features/search/solution.js +++ b/graph-search/src/features/search/solution.js @@ -29,7 +29,7 @@ function formatSolution(solution) { } export function Solution(props) { - const search = dijkstras; + const search = recursiveDFS; const firstSolution = search(firstExample, 'a', 'd'); const secondSolution = search(secondExample, '123', '321'); return (