From 64210694e230681ee5bada6d30aa725ab8fa1438 Mon Sep 17 00:00:00 2001 From: John Costa Date: Mon, 29 Jul 2024 21:59:23 +0100 Subject: [PATCH] feat(dijkstra): working algorithm with array list Need to make it better and use an actual priority-queue --- AdventOfCode2023/src/graph/graph.zig | 124 +++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 AdventOfCode2023/src/graph/graph.zig diff --git a/AdventOfCode2023/src/graph/graph.zig b/AdventOfCode2023/src/graph/graph.zig new file mode 100644 index 0000000..f43d265 --- /dev/null +++ b/AdventOfCode2023/src/graph/graph.zig @@ -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); +}