Skip to content

Commit 23c3de3

Browse files
committed
v1.0.7
1 parent 4385ec5 commit 23c3de3

14 files changed

+207
-48
lines changed

.idea/workspace.xml

Lines changed: 44 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Algorithm Visualizer.exe

4.23 KB
Binary file not shown.

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ of nodes that starts from the source and ends at the target. The nodes in the pa
3434
edges each have a respective weight. The shortest path will be the path of nodes between the source and target that
3535
minimizes the total weight of all edges connecting those path nodes.
3636

37-
> Example:
38-
Let our graph have nodes that represent US cities. The edges between those nodes are select roads that travel
37+
> Let our graph have nodes that represent US cities. The edges between those nodes are select roads that travel
3938
between the cities. Each road has an associated distance, this is the edge weight. Let's say our source node is
4039
Boston and our target node is Dallas. We want the shortest path between the two cities, given our graph of cities
4140
and roads. Such a path will indeed span between the two cities, but it will also be the least distance out of all
@@ -59,8 +58,7 @@ subgraph of some parent graph such that:
5958

6059
Considering all this, a minimum spanning tree is just a tree that minimizes the total edge weights within it.
6160

62-
>Example:
63-
Using the cities and roads analogy, a MST would be a collection of roads that together connect every city and the
61+
> Using the cities and roads analogy, a MST would be a collection of roads that together connect every city and the
6462
sum of distances of these roads is the least possible such that all cities are still linked together with this network
6563
of roads.
6664

@@ -89,6 +87,7 @@ The application is launched by executing _Algorithm.Visualizer.exe_
8987
- Dijkstra's Algorithm
9088
- A* Search
9189
- Bellman-Ford
90+
- Floyd-Warshall
9291
- Reverse Delete
9392
- Kruskal's Algorithm
9493
- Prim's Algorithm
@@ -119,6 +118,9 @@ The application is launched by executing _Algorithm.Visualizer.exe_
119118
> Fast Bellman-Ford algorithm on a medium graph
120119
> <img src="./media/bellman_ford.gif" width=800>
121120
121+
> Fast Floyd-Warshall algorithm on a small graph
122+
> <img src="./media/floyd_warshall.gif" width=800>
123+
122124
> Fast Kruskal's algorithm on a large graph
123125
> <img src="./media/kruskal.gif" width=800>
124126

media/floyd_warshall.gif

1.91 MB
Loading

src/Algorithm_Visualizer.jar

2.54 KB
Binary file not shown.

src/main/java/GUI.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public GUI() {
3838
initMenuPanel();
3939
frame.pack();
4040
frame.setVisible(true);
41+
frame.setResizable(false);
4142
}
4243

4344

@@ -119,7 +120,7 @@ private void initMenuPanel() {
119120
chooseAlgName = new JComboBox<>();
120121
Defs.algNames.forEach(chooseAlgName::addItem);
121122
// Default. See GraphPanel constuctor
122-
chooseAlgName.setSelectedItem("Dijkstra");
123+
chooseAlgName.setSelectedItem("A*");
123124
chooseAlgName.setFont(new Font("Ariel", Font.PLAIN, 18));
124125
chooseAlgName.addActionListener(event -> chooseAlgNameActions());
125126
menu.add(chooseAlgName);

src/main/java/GraphPanel.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,13 @@ public MOUSE_STATE next() {
6565
public GraphPanel() {
6666

6767
// Defaults
68-
algName = "Dijkstra";
68+
algName = "A*";
6969
speed = Defs.speedST.get("Fast");
7070
graphSize = "Large";
7171
isShortPathAlg = Defs.isShortPathAlg.get(algName);
7272
nodeCount = Defs.nodeCountST.get(graphSize);
7373
visitedEdges = ConcurrentHashMap.newKeySet();
74+
path = new int[nodeCount];
7475

7576
GraphGenerator.generateGraph(this);
7677
mouseState = MOUSE_STATE.SOURCE_NODE;
@@ -83,7 +84,6 @@ public GraphPanel() {
8384

8485
@Override
8586
public void mouseClicked(MouseEvent me) {
86-
8787
super.mouseClicked(me);
8888

8989
// Block node selection during animation
@@ -129,7 +129,7 @@ protected void paintComponent(Graphics g) {
129129
g2D.setRenderingHint(RenderingHints.KEY_RENDERING,
130130
RenderingHints.VALUE_RENDER_QUALITY);
131131
g2D.setFont(new Font("Ariel", Font.PLAIN, 18));
132-
if (isShortPathAlg) {
132+
if (isShortPathAlg && null != algorithm && !algorithm.isAlive()) {
133133
g2D.drawString("Click nodes to define a source and target",
134134
(float) (GUI.WINDOW_WIDTH * 0.39), 15);
135135
g2D.setFont(new Font("Ariel", Font.PLAIN, 16));
@@ -183,6 +183,7 @@ protected void paintComponent(Graphics g) {
183183
g2D.setStroke(new BasicStroke(4f));
184184
int currentNode = targetNode;
185185
int prevNode = path[targetNode];
186+
// Tranverse path back to source node
186187
while (prevNode != Integer.MAX_VALUE) {
187188
double currentNodeX = nodeCoords.get(currentNode)[0];
188189
double currentNodeY = nodeCoords.get(currentNode)[1];
@@ -191,8 +192,6 @@ protected void paintComponent(Graphics g) {
191192
g2D.draw(new Line2D.Double(currentNodeX, currentNodeY,
192193
prevNodeX, prevNodeY));
193194
g2D.fill(nodeShapes.get(prevNode));
194-
195-
// Tranverse path back to source node
196195
currentNode = prevNode;
197196
prevNode = path[prevNode];
198197
}
@@ -228,7 +227,7 @@ public void resetAnimation() {
228227

229228

230229
/**
231-
* If an algorithm is running, it is unpaused. Otherwise, a new process
230+
* If an algorithm is running, then it is unpaused. Otherwise, a new process
232231
* of the currently selected algorithm is started.
233232
*/
234233
protected void startAlgorithm() {
@@ -253,6 +252,9 @@ protected void startAlgorithm() {
253252
case "Bellman-Ford":
254253
algorithm = new BellmanFord(this);
255254
break;
255+
case "Floyd-Warshall":
256+
algorithm = new FloydWarshall(this);
257+
break;
256258
case "Reverse Delete":
257259
algorithm = new ReverseDelete(this);
258260
break;

src/main/java/util/Defs.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class Defs {
1212
// All algorithms available with the visualizer
1313
public static final List<String> algNames = List.of("Depth-First Search",
1414
"Breadth-First Search", "Dijkstra", "A*", "Bellman-Ford",
15-
"Reverse Delete", "Kruskal", "Prim");
15+
"Floyd-Warshall", "Reverse Delete", "Kruskal", "Prim");
1616

1717
// Maps algorithms to whether they should use minimum connected graphs or not.
1818
public static final Map<String, Boolean> isShortPathAlg =
@@ -22,6 +22,7 @@ public class Defs {
2222
Map.entry("Dijkstra", true),
2323
Map.entry("A*", true),
2424
Map.entry("Bellman-Ford", true),
25+
Map.entry("Floyd-Warshall", true),
2526
Map.entry("Reverse Delete", false),
2627
Map.entry("Kruskal", false),
2728
Map.entry("Prim", false)));
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package main.java.util.algorithms;
2+
3+
4+
import main.java.GraphPanel;
5+
import org.jgrapht.graph.DefaultWeightedEdge;
6+
7+
import java.util.Arrays;
8+
import java.util.LinkedList;
9+
import java.util.List;
10+
import java.util.ListIterator;
11+
import java.util.concurrent.TimeUnit;
12+
13+
/**
14+
* Implements a Floyd-Warshall algorithm to find a shortest path from a
15+
* {@link GraphPanel#sourceNode} to a {@link GraphPanel#targetNode}.
16+
*
17+
* @author Ryan Albertson
18+
*/
19+
public class FloydWarshall extends Algorithm {
20+
21+
22+
/**
23+
* Constructor.
24+
* @param gPanel {@link GraphPanel}.
25+
*/
26+
public FloydWarshall(GraphPanel gPanel) {
27+
28+
super(gPanel);
29+
}
30+
31+
32+
/**
33+
* @param skip The animation pause will be effectively removed if skip is true.
34+
* @see Algorithm#animate()
35+
*/
36+
protected void animate(boolean skip) {
37+
38+
int speed = gPanel.speed > 0 ? gPanel.speed - 350 : gPanel.speed;
39+
if (skip) speed = 0;
40+
try {
41+
// Update animation
42+
gPanel.repaint();
43+
TimeUnit.MILLISECONDS.sleep(speed);
44+
} catch (InterruptedException e) {
45+
e.printStackTrace();
46+
}
47+
48+
checkForPause();
49+
}
50+
51+
52+
public void runAlgorithm(Integer node) {
53+
54+
// distanceTo[i][j] is the min weight path from node i to node j
55+
double[][] distanceTo = new double[gPanel.nodeCount][gPanel.nodeCount];
56+
for (double[] row : distanceTo) Arrays.fill(row, Double.MAX_VALUE);
57+
58+
// Pointers to next nodes in paths within the graph
59+
int[][] next = new int[gPanel.nodeCount][gPanel.nodeCount];
60+
for (int[] row : next) Arrays.fill(row, -1);
61+
62+
// Init the weights of direct edges between node pairs
63+
for (DefaultWeightedEdge edge : gPanel.graph.edgeSet()) {
64+
int edgeSource = gPanel.graph.getEdgeSource(edge);
65+
int edgeTarget = gPanel.graph.getEdgeTarget(edge);
66+
67+
distanceTo[edgeSource][edgeTarget] = gPanel.graph.getEdgeWeight(edge);
68+
next[edgeSource][edgeTarget] = edgeTarget;
69+
// Consider the other direction of current edge
70+
distanceTo[edgeTarget][edgeSource] = distanceTo[edgeSource][edgeTarget];
71+
next[edgeTarget][edgeSource] = edgeSource;
72+
}
73+
74+
// For every node in the graph, iterate through every pair of nodes
75+
for (int k = 0; k < gPanel.nodeCount; k++) {
76+
// Check if we can reroute path i~j through node k
77+
for (int i = 0; i < gPanel.nodeCount; i++) {
78+
for (int j = 0; j < gPanel.nodeCount; j++) {
79+
// Bypass animation pause if no updates were made
80+
boolean skipAnimation = true;
81+
// Update distance if shorter path is found between i~j
82+
if (distanceTo[i][k] + distanceTo[k][j] < distanceTo[i][j]) {
83+
distanceTo[i][j] = distanceTo[i][k] + distanceTo[k][j];
84+
// Update pointer for this path
85+
next[i][j] = next[i][k];
86+
87+
DefaultWeightedEdge edge = gPanel.graph.getEdge(i, k);
88+
if (null == edge) edge = gPanel.graph.getEdge(k, i);
89+
if (null != edge) {
90+
gPanel.visitedEdges.add(edge);
91+
skipAnimation = false;
92+
}
93+
edge = gPanel.graph.getEdge(k, j);
94+
if (null == edge) edge = gPanel.graph.getEdge(j, k);
95+
if (null != edge) {
96+
gPanel.visitedEdges.add(edge);
97+
skipAnimation = false;
98+
}
99+
} else {
100+
DefaultWeightedEdge edge = gPanel.graph.getEdge(i, j);
101+
if (null == edge) edge = gPanel.graph.getEdge(j, i);
102+
if (null != edge) {
103+
gPanel.visitedEdges.add(edge);
104+
skipAnimation = false;
105+
}
106+
}
107+
// Check if user has stopped or paused algorithm
108+
animate(skipAnimation);
109+
if (isStopped()) return;
110+
}
111+
}
112+
gPanel.visitedEdges.clear();
113+
114+
int u = gPanel.sourceNode;
115+
int v = gPanel.targetNode;
116+
// If path exists
117+
if (next[u][v] != -1) {
118+
// Build path and then trace in reverse order for animation
119+
List<Integer> path = new LinkedList<>();
120+
path.add(u);
121+
while (u != v) {
122+
u = next[u][v];
123+
path.add(u);
124+
}
125+
ListIterator<Integer> it = path.listIterator(path.size());
126+
Integer curr = path.get(path.size() - 1);
127+
while (it.hasPrevious() && !curr.equals(gPanel.sourceNode)) {
128+
int prev = it.previous();
129+
gPanel.path[curr] = prev;
130+
curr = prev;
131+
}
132+
133+
// Check if user has stopped or paused algorithm
134+
animate(true);
135+
if (isStopped()) return;
136+
}
137+
}
138+
}
139+
140+
141+
public void runAlgorithm() {
142+
143+
// This signature isn't needed for this algorithm.
144+
}
145+
}

target/classes/main/java/GUI.class

31 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
159 Bytes
Binary file not shown.
57 Bytes
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)