Графы

В этой главе мы поговорим о том, что такое граф и как хранить его в компьютере. Само по себе понятие граф лучше всего представлять графически: это набор вершин (обозначаемые как ) и набор ребер между этими вершинами (обозначаем как ). График 1

Некоторые вершины (между которыми есть ребро) соединены, некоторые нет. Можно представить, для примера, карту дорог, где города - это вершины, а дороги между ними - это ребра.

В компьютере весь этот граф может храниться разными способами. Первый и самый очевидный способ - это хранить два множества: множества вершин (обычно вершины нумеруют числами от 1 до и не хранят это множество явно) и множество пар, которые будут обозначать двойки вершин, между которыми есть ребра. Но этот способ плохо работает, когда нам нужно обходить или искать что-нибудь в графе. Поэтому чаще всего используют один из двух способов хранения графа в памяти: матрица смежности или списки связности.

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

Матрица смежности - это матрица размера x , где в ячейке (, ) стоит 1, если между вершинами и есть ребро, а иначе 0.

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

# строим матрицу по множеству ребер
def build_matrix(edges, N): # edges - это пары вершины, между которыми есть ребро
    matrix = [[0] * N for i in xrange(N)] # матрица из нулей размером N x N
    for u, v in edges:      # обходим все ребра и помечаем нужные ячейки в матрице
        matrix[u][v] = 1
    return matrix
# поиск всех соседей данной вершины
def find_neighbours(matrix, v): 
    return [u for u in xrange(len(matrix[v])) if matrix[v][u] == 1] 
    # все вершины, у которых стоит единичка, 
    # обозначающая ребро между ней и v

Как видите, построение этой матрицы очень просто, и поиск соседних вершин выполняется так же без особых усилий - мы смотрим или изменяем нужную ячейку и все. Но за простоту надо платить, и платим мы за нее скоростью работы. Сама матрица имеет размер и это уже операций на построение. Потом поиск всех вершин, у которых есть ребро с данной, занимает тоже много времени . Естественно, при очень большом колиечестве ребер, то есть примерно равном , это асимптотика кажется очень неплохой, но, к сожалению, в реальной жизни таких графов почти не бывает (вспомним ту же карту дорог - если бы между каждым городом было по дороге, то производители асфальта давно бы уже стали миллиардерами). Таким образом, оставим матрицу смежности для графов с большим количеством ребер и перейдем с спискам связности.

Списки связности

Списки связности в отличие от матрицы смежности зависят от количества ребер в хранимом графе. Для каждой веришны создается список, в котором содержатся все вершины, для которых есть ребро с нужной. По сути, в этом списке хранятся все ребра выходяющие из какой-то одной вершины. Суммируя все списки по всем вершинам, получаем все ребра в графе, тем самым вся структура занимает памяти и требует операций для построения. Поиск соседних вершин в худшем случае тоже может занимать (потому что у вершины может быть как раз ровно сосед), но фокус в том, что если эту операцию поиска провести для всех вершин в графе, то мы не сможем получить более операций (можно рассуждать так же как и с асимптотикой построения списков - для каждой вершины мы делаем не более операций, где - количество выходящих из вершины ребер. Складывая все получаем как раз ). Опять приведем реализацию построения структуры и поиск соседов для данной вершины.

# строим списки связности по множеству ребер
# edges - это пары вершины, между которыми есть ребро
def build_connectivity_lists(edges, N): 
    lists = [[] for i in xrange(N)] # создаем N пустых списков
    for u, v in edges:
        # добавляем в список вершины u смежную с ней v
        lists[u].append(v) 
    return lists
# поиск всех соседей данной вершины
def find_neighbours(lists, v): 
     # сама струтура уже хранит соседей вершины
    return lists[v]

Алгоритмы на графах

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

results matching ""

    No results matching ""