Лекция №6. Деревья и графы

Хорошевич Павел Александрович

Графы

Примеры применения графов

сетевое оборудование, электрические цепи.

Задача “Социальная сеть”

Определения

Множеством называется некая совокупность элементов.

Все элементы множества - различны, т.е. в любом множестве нет двух копий одного и того же элемента.

Обозначение - большие латинские буквы: \(A\), \(B\), \(C\) и т.д.

Перечисление элементов множества: \(A=\{2, 3, 5\}\)

Принадлежность элемента множеству: \(2 \in A\), \(6 \notin A\)

Пустое множество: \(\emptyset\)

Мощность множества - количество элементов в нём. Обозначение \(|A|\)

Граф - это упорядоченная пара \(G=(V, E)\), состоящая из не пустого множества вершин \(V\) и множества рёбер \(E\).

Вершины графа называются смежными, если у них есть общее ребро.

\[ V = \{0, 1, 2, 3\} \]

\[ E=\{\{0, 1\}, \{0, 2\}, \{2, 3\}, \{1, 2\}\} \]

Визуализация

Визуализация

Ориентированные и неориентированные графы

Ориентированный граф

Ориентированный граф

Неориентированный граф

Неориентированный граф

Взвешенные и невзвешенные графы

Взвешенный граф

Взвешенный граф

Невзвешенный граф

Невзвешенный граф

Структуры данных для графов

Матрица смежности M

Элемент \(M[i, j]=1\), если \((i, j)\) является ребром графа \(G\) и \(M[i, j]=0\) в противоположном случае.

Матрица смежности

Матрица смежности

Список смежности

Списком смежности для вершины \(i\) называется список всех вершин, смежных с вершиной \(i\), причём определённым образом упорядоченный.

Список смежности

Список смежности

Абстрактный тип данных Graph

Опишем интерфейс класса Graph.

  • Конструктор: Graph(n): n - максимальное количество вершин в графе.
  • add_edge(v, w): Добавить ребро между вершинами v и w
  • has_edge(v, w): Являются ли вершины v и w смежными. Возвращает логическое значение.
  • neighbors(v): Возвращает список вершин, смежных с вершиной v
  • set_value(v, st): Сохранить строку st в вершине v
  • get_value(v): Получить строку, сохранённую в вершине v
from ds import Graph

g = Graph(4) # создаём объект типа Graph
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 3)

g.abj() # вывод на экран матрицы смежности

print("Вершины смежные с 0:", g.neighbors(0))
Матрица смежности
    0 1 2 3
   --------
 0| 0 1 1 0
 1| 1 0 1 0
 2| 1 1 0 1
 3| 0 0 1 0
Вершины смежные с 0:  [1, 2]

Алгоритмы обхода графов

Обход графа - систематизированное посещение каждой вершины и каждого ребра графа.

Ключевая идея - возможность отмечать вершины как посещённые и не посещённые. Вершины будем помещать в один из контейнеров на выбор - стек или очередь.

  • Обход в глубину
  • Обход в ширину

Обход в глубину (DFS)

DFS

DFS

Визуализация DFS

  1. Начинаем обход с выбранной вершины, помечаем её как посещённую и рекурсивно переходим к непосещённым соседям.
  2. При каждом переходе углубляемся до тех пор, пока не достигнем вершины без не посещённых соседей.
  3. После завершения пути возвращаемся назад по стеку вызовов, продолжая обход с других не посещённых вершин.

Блок-схема

Блок-схема

Программная реализация:

from ds import Graph
from traversal import DFS

g = Graph(4)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 3)

DFS(g, 2)

Обход в ширину (BFS)

BFS

BFS

Визуализация BFS

  1. Начинаем обход с выбранной вершины, добавляя её в очередь и помечая как посещённую.
  2. Извлекаем вершину из очереди, посещаем всех её не посещённых соседей и добавляем их в очередь.
  3. Повторяем процесс, пока очередь не станет пустой, обходя граф в ширину.

Блок-схема

Блок-схема

Программная реализация:

from ds import Graph
from traversal import BFS

g = Graph(4)
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 3)

BFS(g, 1)

Деревья

Примеры применения

Определения

Дерево - это совокупность элементов (узлов) и отношений, образующих иерархическую структуру узлов.

Дерево

Дерево

Дерево - это частный случай графа.

Один из узлов дерева называется корнем - узел без родительских узлов.

Если узел не имеет дочерних узлов, он называется листом.

Дерево называется бинарным, если каждый узел содержит не больше двух дочерних узлов.

Примеры

Примеры

Абстрактный тип данных Node

Опишем тип данных Node для создания узла дерева.

  • Node(value, left, right): Создать узел, хранящий значение value, left - ссылка на узел слева, right - ссылка на узел справа.
  • is_leaf(): Является ли узел листом. Возвращает логическое значение.
  • value: Получить значение.
  • left: Получить ссылку на узел слева.
  • right: Получить ссылку на узел справа.
n1 = Node(1)
n2 = Node(2)
op = Node('+', n1, n2)
print(op.right.value)
print(op.is_leaf())
print(n1.is_leaf())

Обход бинарного дерева: симметричный обход

Левое поддерево → Корень → Правое поддерево

inorder

inorder

Блок-схема

Блок-схема

Прямой обход

Корень → Левое поддерево → Правое поддерево

prorder

prorder

Обратный обход

Левое поддерево → Правое поддерево → Корень

postorder

postorder

Пример: вычисление арифметического выражения

Дано арифметическое выражение, содержащее только сложение и скобки. Вычислите значение выражения.

Вычислим значение выражения \(3+(7+4)\). На основе выражения построим бинарное дерево.

  • корень - последний выполняемый оператор;

  • листья - числа, над которыми выполняются действия.

from ds import Node
from traversal import inorder, postorder

n7 = Node(7)
n4 = Node(4)
n3 = Node(3)

op1 = Node('+', n7, n4)
root = Node('+', op1, n3)

inorder(root) # симметричный обход
print()
postorder(root) # обратный обход

Результат:

7+4+3 
74+3+ 

Блок-схема

Блок-схема
from ds import Node
from traversal import t_eval

n7 = Node(7)
n4 = Node(4)
n3 = Node(3)

op1 = Node('+', n7, n4)
root = Node('+', op1, n3)

res = t_eval(root)
print(res)