Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi / песни о паскале.pdf
Скачиваний:
62
Добавлен:
26.03.2016
Размер:
5.16 Mб
Скачать

Глава 57

Графомания

Всё кажется, запутано, словно паутина (а паутина — это тоже граф!). Однако выраженное на Паскале это описание выглядит не таким уж страшным.

type PNode =

^TNode;

{ Указатель на запись-узел }

PLink =

^TLink;

{ Указатель на список связей }

TLink =

record

{ Элемент списка связей }

mLink

: PNode;

{ указатель на смежный узел }

mNext : PLink;

{ указатель на следующую запись в списке }

end;

 

 

TNode = record

{ Узел графа (страна) }

mName : Char;

{ Название страны (одна буква) }

mLinks: PLink;

{ список связей с соседями (ребра) }

mNext : PNode;

{ указатель на следующую запись в списке }

end;

 

 

var List : PNode; { список всех стран континента (узлов графа) }

Здесь определены два типа записей: элемент для списка узлов (TNode) и элемент для списка связей (TLink). Соответственно объявлены и два типа указателей на них. Для доступа к графу нужна всего одна глобальная переменная List — указатель на первый элемент во вспомогательном списке. И это всё! Как видите, пока ничего сложного.

Ввод и вывод графа

Мы обрисовали граф в памяти, а это уже полдела. Или ещё полдела. Следующая забота — организовать ввод и вывод графа. Так мы поступали и раньше, изучая множества, массивы и другие сложные типы данных.

Напомню ещё раз кусочек входного файла, с которым мы будем иметь дело.

A B D F I

B A C I H

C B D

Здесь первый символ строки — это имя страны, а последующие — её соседи. Например, в третьей строчке показано, что страна C соседствует со странами B и D.

Сначала обсудим алгоритм ввода графа в общих чертах.

456

Глава 57

Графомания

Разумеется, что файл будем обрабатывать построчно. Взяв первый символ строки, проверим, нет ли во вспомогательном списке узла с таким именем? Возможно, что узел для этой страны уже создан при обработке предыдущих строк (что будет ясно из следующего абзаца). Если узел ещё не создан, создаем его и вставляем во вспомогательный список (обозначим этот узел буквой P).

Далее просматриваем оставшиеся символы строки. Для каждого из них тоже проверяем наличие готового узла. Если его нет, создаем этот узел (назовем его q), вставляем во вспомогательный список, и устанавливаем связь между узлами P и q. Эта связь будет односторонней: от P к q. Но, поскольку связь для каждой пары узлов устанавливается дважды, то, в конце концов, мы получим двусторонние связи. Например, при обработке второй строки файла будет установлена связь B à C, а при обработке третьей — связь B ß C.

Теперь всё сказанное изобразим блок-схемой (рис. 137).

Чтобы облегчить себе дальнейший труд, заготовим две функции и процедуру,

аименно:

функцию для поиска узла по его имени;

функцию для создания нового узла;

процедуру для установки связи между двумя узлами.

Функцию поиска узла по его имени объявим так.

function GetPtr(aName : char): PNode;

Она ищет во вспомогательном списке узел по заданному в параметре имени. В случае успеха, функция вернет указатель на узел, а иначе — NIL.

Функция MakeNode создает новый узел графа с заданным именем, вставляет его во вспомогательный список узлов и возвращает указатель на этот узел.

function MakeNode(aName : Char): PNode;

И, наконец, процедура установки связей Link добавляет в список связей первого узла элемент связи со вторым узлом.

procedure Link(p1, p2 : PNode);

Все три подпрограммы очень просты, поскольку работают со списками.

457

Глава 57

Графомания

Пока не конец файла

Открыть входной файл

Взять первый символ

строки

Узел P существует?

Нет

Да Создать узел P

Перейти к следующей строке

Пока не конец строки

Взять следующий

символ строки

 

 

Нет

Узел q существует?

Создать узел q

 

 

Связать P à q

Рис. 137 – Алгоритм чтения графа

Немногим сложнее будет процедура распечатки графа, она объявлена так.

procedure ExpoData(var F: Text);

Процедура пробегает по вспомогательному списку узлов и спискам связей, распечатывая имена стран и их соседей.

Остальные детали алгоритма пояснены в программе P_57_1.

458

 

 

Глава 57

 

 

 

Графомания

 

 

 

 

 

{ P_57_1 – Ввод и вывод графа }

 

 

type PNode = ^TNode;

{ Указатель на запись-узел }

 

 

PLink = ^TLink;

{ Указатель на список связей }

 

 

TLink = record

{ Тип список связей }

 

 

mLink : PNode;

{ указатель на смежный узел }

 

 

mNext : PLink;

{ указатель на следующую запись в списке }

 

 

end;

 

 

 

TNode = record

{ Тип запись для хранения страны (узла графа) }

 

 

mName : Char;

{ Название страны (одна буква) }

 

 

mLinks: PLink;

{ список связей с соседями (смежными узлами) }

 

 

mNext : PNode;

{ указатель на следующую запись в списке }

 

 

end;

 

 

var List : PNode; { список всех стран континента (узлов графа) }

{ Функция поиска страны (узла графа) по имени страны }

function GetPtr(aName : char): PNode; var p : PNode;

begin

p:= List; { поиск начинается с головы списка } { проходим по элементам списка }

while Assigned(p) do begin

if p^.mName= aName

 

then break

{ нашли! }

else p:= p^.mNext;

{ а иначе следующий }

end;

 

GetPtr:= p;

 

end;

 

{ Функция создает новую страну (узел), вставляет в глобальный список List

и возвращает указатель на новый узел }

function MakeNode(aName : Char): PNode; var p : PNode;

begin

 

New(p);

{ создаем переменную }

p^.mName:= aName;

{ копируем имя }

p^.mLinks:=nil;

{ список связей пока пуст }

p^.mNext:= List;

{ указатель на следующий берем из заголовка }

List:= p;

{ заголовок указывает на новый узел }

MakeNode:= p;

{ результат выполнения функции }

end;

 

459

 

 

 

Глава 57

 

 

 

 

Графомания

 

 

 

 

 

{ Процедура установки связи узла p1 с узлом p2 }

 

 

procedure Link(p1, p2 : PNode);

 

 

 

var p : PLink;

 

 

 

 

begin

 

 

 

 

New(p);

{ создаем переменную-связь }

 

 

p^.mLink:= p2;

{ поле mLink должно указывать на p2 }

 

 

p^.mNext:= p1^.mLinks;

{ указатель на следующий берем из заголовка }

 

 

p1^.mLinks:= p;

{ заголовок указывает на новый узел }

 

 

end;

 

 

 

 

{ Процедура чтения графа из текстового файла }

 

 

procedure ReadData(var F: Text);

 

 

 

var C : Char;

 

 

 

 

p, q : PNode;

 

 

 

 

begin

 

 

 

 

Reset(F);

 

 

 

 

while not Eof(F) do begin

 

 

 

if not Eoln(F) then begin

{ если строка не пуста }

 

 

Read(F, C);

 

{ читаем имя страны }

 

 

C:=UpCase(C);

 

{ перевод в верхний регистр }

 

 

p:= GetPtr(C);

 

{ а может эта страна уже существует? }

 

 

if not Assigned(p)

 

 

 

 

then p:= MakeNode(C);

{ если нет, – создаем }

 

 

while not Eoln(F) do begin

{ чтение стран-соседей до конца строки }

 

 

Read(F, C);

 

 

 

 

C:= UpCase(C);

 

 

 

 

if C in ['A'..'Z'] then begin { если это имя страны, а не пробел }

 

 

q:= GetPtr(C);

 

{ проверяем существование страны }

 

 

if not Assigned(q)

{ если не существует, – создаем }

 

 

then q:= MakeNode(C);

 

 

 

Link(p, q);

 

{ связываем страну p с q }

 

 

end

 

 

 

end end;

Readln(F); { переход на следующую строку файла } end;

end;

460

Глава 57

Графомания

{ Процедура распечатки графа }

procedure ExpoData(var F: Text); var p : PNode;

q : PLink;

begin

Rewrite(F);

p:= List; { начало просмотра списка стран (узлов) } while Assigned(p) do begin

 

Write (F, p^.mName);

{ название страны }

 

q:= p^.mLinks;

 

{ начало просмотра списка соседей }

 

while Assigned(q) do begin

 

Write(F, ' ', q^.mLink^.mName); { название соседа }

 

q:= q^.mNext;

 

{ следующий сосед }

 

end;

 

 

 

Writeln(F);

{ конец строки }

 

p:= p^.mNext;

{ следующая страна }

end;

 

 

Close(F);

 

 

end;

 

 

 

var

F_In, F_Out : Text;

{ входной и выходной файла }

begin {--- Главная программа ---}

List:= nil;

 

Assign(F_In, 'P_57_1.in');

 

ReadData(F_In);

{ читаем граф из входного файла }

Assign(F_Out,'P_57_1.out');

 

ExpoData(F_Out);

{ печатаем в выходной файл }

end.

 

Запустив эту программу, я обнаружил на выходе такой результат:

G I H F

E F D

H I G B

C D B

I H G B A

F G E A

D E C A

B H I C A

A I F D B

Это явно отличается от входных данных, разница налицо, неужели ошибка? Да, порядок следования узлов не совпадает. И порядок перечисления связей в

461

Соседние файлы в папке delphi