Графы и деревья
Список ребер
Этот способ задания графов наиболее удобен для внешнего представления входных данных. Пусть каждая строка входного файла содержит информацию об одном ребре ( дуге ):
<номер_начальной_вершины> <номер_конечной_вершины> [<вес_ребра>]
В качестве примера приведем списки ребер ( дуг ), задающие те же три графа с рис. 11.5, рис. 11.6 и рис. 11.7 (см. рис. 11.9).
a b a c b c b d c d c f f d b f |
1 2 1 4 3 1 3 2 3 5 4 3 |
a b 1 a c 10 b c 2 b d 10 c d 3 |
Если задается ориентированный граф, то номера вершин понимаются как упорядоченная пара, а если граф неориентированный - как неупорядоченная.
Списки смежности
Этот способ задания графов подразумевает, что для каждой вершины будет указан список всех смежных с нею вершин (для орграфа - список вершин, являющихся концами исходящих дуг ). Конкретный формат входного файла, содержащего списки смежности, необходимо обговорить отдельно. Например, в нашем случае начальная вершина отделена от списка смежности двоеточием:
<номер_начальной_вершины>: <номера_смежных_вершин>
Наиболее естественно применять этот способ для задания орграфов, однако и для остальных вариантов он тоже подходит.
В качестве примера приведем списки смежности, задающие все те же три графа, изображенные на рис. 11.5, рис. 11.6 и рис. 11.7 (см. рис. 11.10).
a: b c b: c d f c: d f d: f |
1: 2 4 3: 1 2 5 4: 3 |
b: a 1 c 2 d 10 c: a 10 d 3 |
Иерархический список
Собственно, этот способ представления графов является всего лишь внутренней реализацией списка смежности: в одном линейном списке содержатся номера "начальных вершин ", а в остальных - номера смежных вершин или указатели на эти вершины. В качестве примера (см. рис. 11.11) приведем иерархический список, задающий орграф, изображенный на рис. 11.6.
type uk_versh = ^vershina; uk_duga = ^duga vershina = record number : integer; sled_vershina : uk_versh; spisok_dug : uk_duga end; duga = record konec_dugi : uk_versh; sled_duga : uk_duga; end;
Очевидное преимущество такого способа представления графов заключается в экономичном использовании памяти. И даже небольшая избыточность данных, к которой приходится прибегать в случае неориентированного графа, задавая каждое ребро как две дуги, искупается гибкостью всей структуры, что особенно удобно при необходимости частых перестроений в процессе работы программы.
Если в приведенные описания типов данных добавить поля, которые могли бы хранить веса вершин и дуг, то таким же способом можно задавать и взвешенные графы ( орграфы ).
Деревья
Дерево - это частный случай графа, наиболее широко применяемый в программировании.
Основные определения
Существует довольно много равносильных определений деревьев, вот лишь некоторые из них.
- Дерево - это связный граф без циклов.
- Дерево - это связный граф, в котором при N вершинах всегда ровно N-1 ребро.
- Дерево - это граф, между любыми двумя вершинами которого существует ровно один путь.
Аналогичным образом определяется и ориентированное дерево - как орграф, в котором между любыми двумя вершинами существует не более одного пути.
Дерево | Вершины | Ребра (дуги) |
---|---|---|
Армия | Солдаты и офицеры | Иерархия (командир - подчиненный) |
Династия (родословная по мужской линии) | Монархи | Отношение "отец - сын" |
Мы будем изучать и использовать только один частный случай ориентированных деревьев - корневые деревья (см. рис. 11.12).
Корневое дерево - это ориентированное дерево, в котором можно выделить вершины трех видов: корень, листья (другое их название: терминальные вершины ) и остальные вершины ( нетерминальные ); причем должны выполняться два обязательных условия:
- из листьев не выходит ни одна дуга ; из других вершин может выходить сколько угодно дуг ;
- в корень не заходит ни одна дуга ; во все остальные вершины заходит ровно по одной дуге.
Традиционно в математике и в родственных ей науках (в том числе и в теоретическом программировании) деревья "растут" вниз головой: это делается просто для удобства наращивания листьев в случае необходимости. Таким образом, на рисунках корень дерева оказывается самой верхней вершиной, а листья - самыми нижними.
Предок вершины v - это вершина, из которой исходит дуга, заходящая в вершину v. Потомок вершины v - это вершина, в которую заходит дуга, исходящая из вершины v. В этих терминах можно дать другие определения понятиям корень и лист: у корня нет предков, у листа нет потомков.
Бинарное дерево - это корневое дерево, каждая вершина которого имеет не более двух потомков. В таком случае иногда говорят о левом потомке и правом потомке для текущей вершины.
Высота корневого дерева - это максимальное количество дуг, отделяющих листья от корня. Если дерево не взвешенное, то его высота - это просто расстояние от корня до самого удаленного листа.
И в заключение мы приведем определение, связывающее произвольные графы с деревьями более плотно.
Каркас графа - это дерево, полученное после выбрасывания из графа некоторых ребер (см. рис. 11.13).
Примером каркаса является ( корневое ) дерево кратчайших путей от некоторой выделенной вершины (она будет корнем каркаса ) до всех остальных вершин графа.