163 lines
4.0 KiB
ObjectPascal
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.
|