feat(dijkstra): working algorithm with array list

Need to make it better and use an actual priority-queue
This commit is contained in:
2024-07-29 21:59:23 +01:00
parent c05d558756
commit 64210694e2

View File

@ -0,0 +1,124 @@
const std = @import("std");
const expect = std.testing.expect;
const Order = std.math.Order;
const print = std.debug.print;
const log = std.log;
const mem = std.mem;
const Edge = struct {
in_node: *Node,
out_node: *Node,
distance: usize,
};
const Node = struct {
out_edges: []const Edge,
label: []const u8,
work_distance: usize,
};
const Graph = struct {
nodes: []*Node,
};
// 1 function Dijkstra(Graph, source):
// 2
// 3 for each vertex v in Graph.Vertices:
// 4 dist[v] ← INFINITY
// 5 prev[v] ← UNDEFINED
// 6 add v to Q
// 7 dist[source] ← 0
// 8
// 9 while Q is not empty:
// 10 u ← vertex in Q with minimum dist[u]
// 11 remove u from Q
// 12
// 13 for each neighbor v of u still in Q:
// 14 alt ← dist[u] + Graph.Edges(u, v)
// 15 if alt < dist[v]:
// 16 dist[v] ← alt
// 17 prev[v] ← u
// 18
// 19 return dist[], prev[]
fn find_shortest_index(list: *std.ArrayList(*const Node)) usize {
var shortestIndex: usize = 0;
for (list.items, 0..) |item, index| {
if (item.work_distance < list.items[shortestIndex].work_distance) {
shortestIndex = index;
}
}
return shortestIndex;
}
fn dijkstra(allocator: std.mem.Allocator, graph: Graph, source: *Node, destination: *Node) !usize {
var work_map = std.AutoHashMap(*const Node, *const Node).init(allocator);
// this is for sure the wrong data structure
// properly look at 'priority queue'.
var work_queue = std.ArrayList(*const Node).init(allocator);
for (graph.nodes) |node| {
node.*.work_distance = 99999999999;
try work_queue.append(node);
}
source.work_distance = 0;
while (work_queue.items.len > 0) {
const closest_node_index = find_shortest_index(&work_queue);
const closest_node = work_queue.orderedRemove(closest_node_index);
for (closest_node.out_edges) |edge| {
const neighbor = edge.out_node;
const alternative_path = closest_node.work_distance + edge.distance;
if (alternative_path < neighbor.work_distance) {
neighbor.*.work_distance = alternative_path;
try work_map.put(neighbor, closest_node);
}
}
}
return destination.work_distance;
}
// B
// 4/ 1\
// A D
// 3\ 5/
// C
test "Can find shortest path between A and D" {
const page_allocator = std.heap.page_allocator;
var a = Node{ .out_edges = undefined, .label = "A", .work_distance = 0 };
var b = Node{ .out_edges = undefined, .label = "B", .work_distance = 0 };
var c = Node{ .out_edges = undefined, .label = "C", .work_distance = 0 };
var d = Node{ .out_edges = undefined, .label = "D", .work_distance = 0 };
const ab = Edge{ .in_node = &a, .out_node = &b, .distance = 4 };
const bd = Edge{ .in_node = &b, .out_node = &d, .distance = 1 };
const ac = Edge{ .in_node = &a, .out_node = &c, .distance = 3 };
const cd = Edge{ .in_node = &c, .out_node = &d, .distance = 5 };
a.out_edges = &[_]Edge{ ab, ac };
b.out_edges = &[_]Edge{bd};
c.out_edges = &[_]Edge{cd};
var nodes = [_]*Node{ &a, &b, &c, &d };
const graph = Graph{ .nodes = &nodes };
const shortest_path_a_d = try dijkstra(page_allocator, graph, &a, &d);
const shortest_path_b_d = try dijkstra(page_allocator, graph, &b, &d);
const shortest_path_a_c = try dijkstra(page_allocator, graph, &a, &c);
try expect(shortest_path_a_d == 5);
try expect(shortest_path_b_d == 1);
try expect(shortest_path_a_c == 3);
}