RosettaCodeData/Task/Dijkstras-algorithm/Pascal/dijkstras-algorithm-1.pas

163 lines
4.0 KiB
ObjectPascal

program dijkstra(output);
type
{ We dynamically build the list of vertices from the edge list,
just to avoid repeating ourselves in the graph input. Vertices are linked
together via their `next` pointers to form a list of all vertices (sorted by
name), while the `previous` pointer indicates the previous vertex along the
shortest path to this one. }
vertex = record
name: char;
visited: boolean;
distance: integer;
previous: ^vertex;
next: ^vertex;
end;
vptr = ^vertex;
{ The graph is specified as an array of these }
edge_desc = record
source: char;
dest: char;
weight: integer;
end;
const
{ the input graph }
edges: array of edge_desc = (
(source:'a'; dest:'b'; weight:7),
(source:'a'; dest:'c'; weight:9),
(source:'a'; dest:'f'; weight:14),
(source:'b'; dest:'c'; weight:10),
(source:'b'; dest:'d'; weight:15),
(source:'c'; dest:'d'; weight:11),
(source:'c'; dest:'f'; weight:2),
(source:'d'; dest:'e'; weight:6),
(source:'e'; dest:'f'; weight:9)
);
{ find the shortest path to all nodes starting from this one }
origin: char = 'a';
var
head_vertex: vptr = nil;
curr, next, closest: vptr;
vtx: vptr;
dist: integer;
edge: edge_desc;
done: boolean = false;
{ allocate a new vertex node with the given name and `next` pointer }
function new_vertex(key: char; next: vptr): vptr;
var
vtx: vptr;
begin
new(vtx);
vtx^.name := key;
vtx^.visited := false;
vtx^.distance := maxint;
vtx^.previous := nil;
vtx^.next := next;
new_vertex := vtx;
end;
{ look up a vertex by name; create it if needed }
function find_or_make_vertex(key: char): vptr; var
vtx, prev, found: vptr;
done: boolean;
begin
found := nil;
if head_vertex = nil then
head_vertex := new_vertex(key, nil)
else if head_vertex^.name > key then
head_vertex := new_vertex(key, head_vertex);
if head_vertex^.name = key then
found := head_vertex
else begin
prev := head_vertex;
vtx := head_vertex^.next;
done := false;
while not done do
if vtx = nil then
done := true
else if vtx^.name >= key then
done := true
else begin
prev := vtx;
vtx := vtx^.next
end;
if vtx <> nil then
if vtx^.name = key then
found := vtx;
if found = nil then begin
prev^.next := new_vertex(key, vtx);
found := prev^.next;
end
end;
find_or_make_vertex := found
end;
{ display the path to a vertex indicated by its `previous` pointer chain }
procedure write_path(vtx: vptr);
begin
if vtx <> nil then begin
if vtx^.previous <> nil then begin
write_path(vtx^.previous);
write('→');
end;
write(vtx^.name);
end;
end;
begin
curr := find_or_make_vertex(origin);
curr^.distance := 0;
curr^.previous := nil;
while not done do begin
for edge in edges do begin
if edge.source = curr^.name then begin
next := find_or_make_vertex(edge.dest);
dist := curr^.distance + edge.weight;
if dist < next^.distance then begin
next^.distance := dist;
next^.previous := curr;
end
end
end;
curr^.visited := true;
closest := nil;
vtx := head_vertex;
while vtx <> nil do begin
if not vtx^.visited then
if closest = nil then
closest := vtx
else if vtx^.distance < closest^.distance then
closest := vtx;
vtx := vtx^.next;
end;
if closest = nil then
done := true
else if closest^.distance = maxint then
done := true;
curr := closest;
end;
writeln('Shortest path to each vertex from ', origin, ':');
vtx := head_vertex;
while vtx <> nil do begin
write(vtx^.name, ':', vtx^.distance);
if vtx^.distance > 0 then begin
write(' (');
write_path(vtx);
write(')');
end;
writeln();
vtx := vtx^.next;
end
end.