From 5ac7536c516bd04ba0959ef11db0aec7cd03186b Mon Sep 17 00:00:00 2001 From: Sanan507 <227714367+Sanan507@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:20:56 +0000 Subject: [PATCH] feat(pathfinding): Add Greedy Best-First Search Algorithm --- .../pathfinding/GreedyBFSModel.java | 55 +++++++++++++++++++ .../pathfinding/PathfindingFactory.java | 10 +++- .../visualizer/utils/ComplexityCatalog.java | 9 +++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/GreedyBFSModel.java diff --git a/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/GreedyBFSModel.java b/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/GreedyBFSModel.java new file mode 100644 index 0000000..b64ff4f --- /dev/null +++ b/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/GreedyBFSModel.java @@ -0,0 +1,55 @@ +package com.algorithmrace.visualizer.algorithms.pathfinding; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class GreedyBFSModel extends PathfindingModel { + private final PriorityQueue openSet = + new PriorityQueue<>(Comparator.comparingDouble(c -> c.hCost)); + + public GreedyBFSModel() { + super("Greedy Best-First"); + } + + private double heuristic(GridCell a, GridCell b) { + return Math.abs(a.row - b.row) + Math.abs(a.col - b.col); + } + + @Override + public void step() { + if (isDone() || openSet.isEmpty()) { + markDone(); + return; + } + GridCell current = openSet.poll(); + if (current == end) { + reconstructPath(end); + markDone(); + return; + } + if (current.state != CellState.START) { + current.state = CellState.VISITED; + } + addStep(); + for (GridCell nb : getNeighbors(current)) { + if (nb.state == CellState.EMPTY || nb.state == CellState.END) { + nb.hCost = heuristic(nb, end); + nb.parent = current; + if (nb.state == CellState.EMPTY) { + nb.state = CellState.FRONTIER; + } + openSet.add(nb); + } + } + } + + @Override + public void reset() { + openSet.clear(); + resetStats(); + if (start != null) { + start.hCost = heuristic(start, end); + openSet.add(start); + } + } +} diff --git a/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/PathfindingFactory.java b/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/PathfindingFactory.java index f2f38b8..54aac29 100644 --- a/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/PathfindingFactory.java +++ b/backend/src/main/java/com/algorithmrace/visualizer/algorithms/pathfinding/PathfindingFactory.java @@ -13,12 +13,20 @@ public static PathfindingModel create(String name) { case "A* Search" -> new AStarModel(); case "Bellman-Ford" -> new BellmanFordModel(); case "Bidirectional BFS" -> new BidirectionalBFSModel(); + case "Greedy Best-First" -> new GreedyBFSModel(); default -> throw new IllegalArgumentException("Unrecognized pathfinding algorithm requested."); }; } public static List allNames() { - return List.of("BFS", "DFS", "Dijkstra", "A* Search", "Bellman-Ford", "Bidirectional BFS"); + return List.of( + "BFS", + "DFS", + "Dijkstra", + "A* Search", + "Bellman-Ford", + "Greedy Best-First", + "Bidirectional BFS"); } } diff --git a/backend/src/main/java/com/algorithmrace/visualizer/utils/ComplexityCatalog.java b/backend/src/main/java/com/algorithmrace/visualizer/utils/ComplexityCatalog.java index 7bbf192..8f22f63 100644 --- a/backend/src/main/java/com/algorithmrace/visualizer/utils/ComplexityCatalog.java +++ b/backend/src/main/java/com/algorithmrace/visualizer/utils/ComplexityCatalog.java @@ -188,6 +188,15 @@ public final class ComplexityCatalog { "Combines travelled distance with a heuristic estimate to guide the search toward the" + " goal.", "choose lowest f = g + h\nrelax neighbors"); + add( + "Greedy Best-First", + "O(b^m)", + "O(b^m)", + "O(b^m)", + "O(b^m)", + "Relies solely on a heuristic distance to the goal, providing a fast but non-optimal" + + " search.", + "choose lowest h\nrelax neighbors"); add( "Bidirectional BFS", "O(b^(d/2))",