I've been trying to implement Dijkstra's algorithm with binary heaps for my algorithms class.The goal is to find the length of the minimum spanning tree found through Dijkstra. I've worked with before, but using Priority queues. I find I'm getting tripped up with the heap management, and if anyone could spot errors. Main method is below:
import java.io.File;
import java.util.regex.*;
import java.util.ArrayList;
public class main {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
File input = new File("filepath\1000.txt");
Scanner scanner = new Scanner(input);
//Regex patterns used to find input size, nodes, and edges
String size = "n=([0-9]*)";
String node = "^[0-9]+";
String edge = "^[\\s]+([0-9]+)[\\s]+([0-9]+)";
Pattern sizePattern = Pattern.compile(size);
Pattern edgePattern = Pattern.compile(edge);
Pattern nodePattern = Pattern.compile(node);
//Loop to fill nodeList
ArrayList <Integer> finalDistances = new ArrayList<>();
MinHeap list = null;
Node currentNode = null;
Edge currentEdge = null;
while(scanner.hasNextLine()) {
String line = scanner.nextLine();
Matcher match1 = sizePattern.matcher(line);
Matcher match2 = nodePattern.matcher(line);
Matcher match3 = edgePattern.matcher(line);
//catches size
if(match1.find()) {
int numberOfNodes = Integer.parseInt(match1.group(1));
//Holds all the nodes to process with Dijkstra's Algo
list = new MinHeap(numberOfNodes);
}
//catches node
if (match2.find()) {
if (!list.contains(Integer.parseInt(line))) {
currentNode = new Node(Integer.parseInt(line));
currentNode.seen = false;
currentNode.distance = Integer.MAX_VALUE;
list.insert(currentNode);
}
else {
currentNode = list.getNode(Integer.parseInt(line));
}
}
//catches edge
if(match3.find()) {
if (!list.contains(Integer.parseInt(match3.group(1)))) {
Node temp = new Node(Integer.parseInt(match3.group(1)));
temp.seen = false;
temp.distance = Integer.MAX_VALUE;
list.insert(temp);
currentEdge = new Edge(Integer.parseInt(match3.group(1)), Integer.parseInt(match3.group(2)));
currentNode.add(currentEdge);
} else {
currentEdge = new Edge(Integer.parseInt(match3.group(1)), Integer.parseInt(match3.group(2)));
currentNode.add(currentEdge);
}
}
}
Node source = list.getNode(0);
source.distance=0;
list.updateNode(source);
int treeLength = 0;
while(!list.isEmpty()) {
currentNode = list.extractMin();
currentNode.seen = true;
ArrayList<Edge> edgeList = currentNode.edges;
for (int i = 0; i < edgeList.size(); i++) {
currentEdge = edgeList.get(i);
if (list.contains(currentEdge.end)) {
int calcDist = currentNode.distance + currentEdge.weight;
if (calcDist < list.getNode(currentEdge.end).distance) {
list.decreaseKey(list.getNode(currentEdge.end), calcDist);
}
}
}
System.out.println(currentNode.toString() + "with distance" +currentNode.distance);
finalDistances.add(currentNode.distance);
}
for (int j = 0; j < finalDistances.size(); j++) treeLength+= finalDistances.get(j);
System.out.println("Tree length is: "+treeLength);
}
//fail safe
catch (Exception ex){
System.out.println("Shit broke!");
}
}
}
The input is just a txt file formated like:
0
25 244
108 275
140 273
159 313
219 199
254 392
369 171
518 271
538 250
568 253
603 307
613 196
638 314
1
24 187
43 182
65 331
155 369
182 222
186 426
224 395
233 72
240 128
250 101
269 251
371 73
409 301
444 40
451 262
464 337
517 393
569 171
586 384
599 221
601 145
611 209
616 330
629 324
644 254
646 316
675 237
684 327
695 439
696 288
The first line is the number of nodes and edges. A number by itself on a line is a Node and the numbers after are edges with weights. My Node class just has a int value, arraylist of edges, and a distance from source. My Edge class has a int value, and int weight.
import java.util.ArrayList;
public class Node {
int value;
ArrayList<Edge> edges;
boolean seen;
int distance;
public Node(int value) {
edges = new ArrayList<>();
this.value = value;
seen = false;
}
public void add (Edge edge) {
edges.add(edge);
}
@Override
//debugging use
public String toString() {
String listOfEdges= "";
//for (int i = 0; i < edges.size(); i++) {
// listOfEdges = listOfEdges + " ," + Integer.toString(edges.get(i).end);
//}
return "Node: "+Integer.toString(value); //+" Edges: "+listOfEdges;
}
}
public class Edge {
int end;
int weight;
public Edge(int end, int weight) {
this.end = end;
this.weight = weight;
}
@Override
//debugging use
public String toString() {
return "Edge: "+end+" Weight: "+weight;
}
}
It seems to be after a point running the algorithm, some vertices remain infinity, and others negative infinity. I'm using two arrays: first is the heap that stores the Nodes, and second is to store the indexes of Nodes in the heap. The full class is below:
private int capacity;
private int currentSize;
private Node[] heap;
private int[] locations;
//private HashMap<Integer, Integer> locations = new HashMap<>();
public MinHeap(int capacity) {
this.capacity = capacity;
heap = new Node[capacity];
locations = new int[capacity];
//heap[0] = new Node(-1);
//heap[0].distance = Integer.MIN_VALUE;
currentSize = 0;
}
private int getParent(int index) {
return (index-1)/2;
}
private int getLeftChild(int index) {
return index*2+1;
}
private int getRightChild(int index) {
return index*2+2;
}
private void swap(int a, int b) {
Node temp = heap[a];
heap[a] = heap[b];
heap[b] = temp;
//maybe bug
}
Node extractMin() {
Node min = heap[0];
Node last = heap[currentSize-1];
swap (0,currentSize-1);
locations[last.value] = 0;
locations[min.value] = -1;
currentSize--;
heapifyDown(0);
System.out.println("heap size: "+currentSize);
return min;
}
void heapifyDown(int index) {
int currentIndex = index;
int leftIndex = getLeftChild(currentIndex);
int rightIndex = getRightChild(currentIndex);
if (leftIndex < currentSize && heap[currentIndex].distance > heap[leftIndex].distance) {
currentIndex = leftIndex;
}
if (rightIndex < currentSize && heap[currentIndex].distance > heap[rightIndex].distance) {
currentIndex = rightIndex;
}
if (currentIndex != index) {
Node newTop = heap[currentIndex];
Node oldTop = heap[index];
locations[newTop.value] = index;
locations[oldTop.value] = currentIndex;
swap(index,currentIndex);
heapifyDown(currentIndex);
}
}
void heapifyUp(int index) {
int parentIndex = getParent(index);
int currentIndex = index;
Node currentNode = heap[currentIndex];
Node parentNode = heap[parentIndex];
while (currentIndex > 0 && heap[parentIndex].distance > heap[currentIndex].distance) {
System.out.println("Swapped: "+heap[getParent(currentIndex)].toString()+" That has a distance of: "+heap[getParent(currentIndex)].distance+ " With: "+heap[currentIndex]+" Which has a distance of: "+heap[currentIndex].distance);
swap(parentIndex,currentIndex);
locations[currentNode.value] = parentIndex;
locations[parentNode.value] = currentIndex;
currentIndex = parentIndex;
parentIndex = getParent(parentIndex);
System.out.println("min: "+heap[0].toString());
}
}
public void decreaseKey(Node node, int distance) {
int location = locations[node.value];
heap[location].distance = distance;
heapifyUp(location);
}
public void insert(Node node) {
//currentSize++;
int index = currentSize;
heap[currentSize] = node;
locations[node.value] = currentSize;
currentSize++;
heapifyUp(index);
}
public boolean contains(int node) {
return locations[node] != 0 && locations[node] != -1;
}
public boolean isEmpty() {
return currentSize==0;
}
public Node getNode(Node node) {
return heap[locations[node.value]];
}
public Node getNode(int nodeValue) {
return heap[locations[nodeValue]];
}
public void updateNode(Node node) {
heap[locations[node.value]] = node;
}
public void print() {
for (int i = 0; i < currentSize; i++) {
System.out.println(heap[i].toString());
}
}
public Node peek() {
return heap[0];
}
public int size() {
return currentSize;
}
}
Any feedback is appreciated.