Диаметр дерева дискретная математика

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

Индивидуальный проект по дискретной математике

alexvilno/calc_tree

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

Нахождение центра, радиуса и диаметра дерева, заданного двоичным кодом

Алгоритм позволяет найти количество вершин, образующих центр, диаметр и радиус дерева, заданного двоичным кодом

Исходные данные берутся из файла input.txt. В первой строке записано одно число n — количество вершин в дереве. Во второй строке располагается двоичный код. Нужно записать три числа в файл output.txt количество вершин, образующих центр, радиус и диаметр.

Использованые структуры данных

Для реализиации списка смежности был использован std::vector

Корректным решением является:

  • Создаем список смежности из двоичного кода
  • Считаем диаметр
  • Считаем радиус
  • Находим центровые вершины

Создание списка смежности из двоичного кода

Для этого используем функцию

std::vectorint>> ReadTree(std::istream &in) < std::vectorint>> tree(1); ReadTreeRec(in, tree, 0); return std::move(tree); >

Она принимает в качестве аргумента входящий поток std::istream &in и создает список смежности на базе std::vector> первая его координата — рассматриваемая вершина, вторая &mdash вершина, которая ей смежна. Например список смежности для дерева, заданного двоичным кодом 0011 будет выглядеть так:

image

То есть по сути, наш список смежности выглядит так:

tree[c][0] = v — вершина c смежна вершине v

Создается он следующим образом:

Читайте также:  Туя полезные свойства дерева

Вызывается рекурсивная функция

void ReadTreeRec(std::istream &in, std::vectorint>> &tree, int v)

Кроме входящего потока, она принимает, собственно, список смежности, который требуется заполнить и параметр int v — индекс вершины

void ReadTreeRec(std::istream &in, std::vectorint>> &tree, int v) < for (char command; in >> command && command != '1';) < int u = tree.size(); tree.emplace_back(); //create in end tree[v].push_back(u); //create and push tree[u].push_back(v); ReadTreeRec(in, tree, u); > >
  • Из входящего потока считывается двоичный код
  • Если считываемый символ command != ‘1’ тогда
    • создается переменная u равная текущему размеру списка смежности — tree.size()
    • создается очередная вершина с индексом v
    • в список смежности tree[v].push_back(u) кладется смежная с ней u
    • для u делается то же самое: tree[u].push_back(v)
    • рекурсивно вызывается эта же функция, но уже от вершины с индексом u

    Довольно логично, что каждую инициализацию переменная u = tree.size() будет равна индексу очередной вершины в списке смежности

    Когда мы получили список смежности, возвращаем его без копирования return std::move(tree)

    Теперь когда мы имеем список смежности для дерева, мы можем посчитать диаметр дерева.

    Считать будем следующим образом:

    • найдем самое большое расстояние от корня дерева до крайней вершины
    • найдем самое большое расстояние от этой вершины до других вершин

    Функция, возвращающая диаметр дерева выглядит так:

    int CountDiameterLength(std::vectorint>> &tree) < int vertices_count = tree.size(); std::vectorint> distances(vertices_count); CountMaxDistanceRec(tree, distances, -1, 0); int new_root = FindVertexWithMaxDistance(distances); distances[new_root] = 0; CountMaxDistanceRec(tree, distances, -1, new_root); return distances[FindVertexWithMaxDistance(distances)]; >

    В качестве аргумента она принимает ссылку на список смежности, созданный ранее

    • int vertices_count = tree.size() запоминаем количество вершин и создаем массив такого размера std::vector distances(vertices_count)
    • заполним этот массив расстояниями от корня дерева с помощью функции void CountMaxDistanceRec(const std::vector &tree, std::vector &distances, int p, int v) — она будет описана ниже
    • от нового «нового корня» (т.е. от той вершины, расстояние до которой будет максимально) также запишем новые расстояния
    • вернем максимальное расстояние, находящееся в массиве расстояний по индексу, возвращаемому функцией FindVertexWithMaxDistance(distances) (реализациия её тривиальна, описываться не будет)

    Как работает void CountMaxDistanceRec(const std::vector> &tree, std::vector &distances, int p, int v) ?

    Она принимает ссылку на список смежности, ссылку на массив расстояний индекс родительского элемента (для корня это -1) и индекс элемента, от которого считаются расстояния

    • Проходимся по всем вершинам, кроме родительской
    • Считаем рекурсивно расстояния до всех других вершин

    То есть грубо говоря, мы прошлись DFS два раза

    Делаем это по формуле radius = (diameter + 1) / 2

    Считаем количество центров

    По формуле center = (diameter) % 2 + 1

    image

    image

    image

    image

    image

    12 0001010110110001011011 

    image

    image

    image

    image

    image

    Для тестов производительности есть функция, возвращающая случайное дерево из n вершин в виде двоичного кода

    std::string tree_generator(int v_count) < std::random_device random_device; std::mt19937 generator(random_device()); std::uniform_int_distribution<> distribution(0, 1); std::string generated = ""; int count_0 = 0; int count_1 = 0; for (int i = 0; i < v_count * 2; ++i) < int x = distribution(generator); if (count_0 >= v_count) < x = 1; generated += x + 48; continue; > if (x == 1 && count_1 < count_0) < generated += x + 48; ++count_1; > else if (x == 1 && count_1 >= count_0) < x = 0; generated += x + 48; ++count_0; > else < generated += x + 48; x == 1 ? ++count_1 : ++count_0; > > return generated; >

    Таблица зависимости количества вершин и времени обработки алгоритмом

    Количество Время(SEC)
    1000 0.002
    10000 0.008
    25000 0.022
    100000 0.071
    500000 0.351
    1000000 0.894
    2000000 1.619
    3000000 2.580
    4000000 2.928
    5000000 3.587
    10000000 8.614
    25000000 20.40
    50000000 76.38

    About

    Индивидуальный проект по дискретной математике

    Источник

    6.5. Диаметр графа

    Сначала необходимо по всем парам вершин вычислить расстояние, а затем найти максимум из этого множества чисел. Задача 6.6. Для графа на рис. 6.25 соответствующие расстояния представлены в табл. 6.5.

    2 3 4 5
    1 6
    Рис. 6.25 10 9 8 7

    Решение . Представлено в табл. 6.5.

    Таблица 6.5
    Матрица расстояний
    1 2 3 4 5 6 7 8 9 10
    1 1 2 2 3 3 2 3 2 1
    2 1 2 3 2 1 2 3 2
    3 1 2 3 2 3 3 2
    4 1 2 3 3 2 1
    5 1 2 3 3 2
    6 1 2 3 3
    7 1 2 3
    8 1 2
    9 1
    10

    Необходимо быть аккуратным при вычислении расстояний, например, расстояние между 1-й и 4-й вершинами равно двум, так как помимо пути (1-2-3-4), имеющего длину 3, также существует путь (1-10-4), имеющий длину 2. Среди всех значений согласно определению диаметра выбирается наибольшее, в данном случае диаметр равен трем.

    6.5.3. Поиск диаметра при операциях над графами

    При операциях над графами иногда значение диаметра проще найти, рассматривая по отдельности участвующие в операциях графы. При этом всегда надо помнить, что диаметр любого несвязного графа равен бесконечности. При операциях объединения по этой причине в случае отсутствия общих вершин диаметр равен всегда бесконечности. При наличии одной общей вершины значение диаметра вычисляется как сумма диаметров двух участвующих в объединении графов. В более сложных случаях необходимо рисовать полученный граф и анализировать его. Важно помнить, что если в задаче не указано, сколько общих вершин имеют объединяемые графы, то необходимо рассматривать все случаи. Задача 6.7. Найти значение диаметра графа, полученного в результате объединения простого цикла на 7 вершинах и полного графа на 5 вершинах, если известно, что они имеют 1 общую вершину. Решение. См. рис. 6.26.

    3 1 Рис. 6.26

    Диаметр простого цикла на 7 вершинах равен [7/2] = 3, а диаметр полного графа всегда равен 1. Значит, диаметр полученного в результате объединения графа равен 3+1 = 4. При операциях сложения двух графов в случае отсутствия общих вершин диаметр всегда будет не более двух. Это связано с тем, что

    Источник

    Алгоритмы на деревьях

    Пусть дан граф [math]G = \langle V, E \rangle [/math] . Тогда диаметром [math]d[/math] называется [math]\max\limits_ dist(v, u)[/math] , где [math]dist[/math] — кратчайшее расстояние между вершинами.

    Алгоритм

    • Возьмём любую вершину [math] v \in V [/math] и найдём расстояния до всех других вершин. [math]d[i] = dist(v, i)[/math]
    • Возьмём вершину [math] u \in V [/math] такую, что [math]d[u] \geqslant d[t][/math] для любого [math]t[/math] . Снова найдём расстояние от [math]u[/math] до всех остальных вершин. Самое большое расстояние — диаметр дерева.

    Расстояние до остальных вершин будем искать алгоритмом [math]BFS[/math] .

    Реализация

    //граф g представлен списком смежности int diameterTree(list> g): v = u = w = 0 d = bfs(g, v) for i = 0, i < n, i++ if d[i] > d[u] u = i d = bfs(g, u) for i = 0, i < n, i++ if d[i] > d[w] w = i return d[w]

    Обоснование корректности

    Будем пользоваться свойством, что в любом дереве больше одного листа. Исключительный случай — дерево из одной вершины, но алгоритм сработает верно и в этом случае.

    После запуска алгоритма получим дерево [math]BFS[/math] .

    В дереве [math]BFS[/math] не существует ребер между вершинами из разных поддеревьев некоторого их общего предка.

    Предположим, существует ребро [math]u, v[/math] между соседними поддеревьями:

    Мы свели задачу к нахождению вершины [math]w[/math] , такой что сумма глубин поддеревьев максимальна.

    Докажем, что одно из искомых поддеревьев содержит самый глубокий лист. Пусть нет, тогда, взяв расстояние от [math]w[/math] до глубочайшего листа, мы можем улучшить ответ.

    Таким образом мы доказали, что нам нужно взять вершину [math]u[/math] с наибольшей глубиной после первого [math]BFS[/math] , очевидно, что ей в пару надо сопоставить вершину [math]w[/math] , такую что [math]dist(u, w)[/math] максимально. Вершину [math]w[/math] можно найти запуском [math]BFS[/math] из [math]u[/math] .

    Оценка времени работы

    Все операции кроме [math]BFS[/math] — [math]O(1)[/math] . [math]BFS[/math] работает за линейное время, запускаем мы его два раза. Получаем [math]O(|V| + |E|)[/math] .

    Центр дерева

    Определения

    Определение:
    Эксцентриситет вершины [math]e(v)[/math] (англ. eccentricity of a vertex) — [math]\max\limits_ dist(v, u)[/math] , где [math]V[/math] — множество вершин связного графа [math]G[/math] .
    Определение:
    Радиус [math]r(G)[/math] (англ. radius) — наименьший из эксцентриситетов вершин графа [math]G[/math] .
    Определение:
    Центральная вершина (англ. central vertex) — вершина графа [math]G[/math] , такая что [math]e(v) = r(G)[/math]
    Определение:
    Центр графа [math]G[/math] (англ. center of a graph) — множество всех центральных вершин графа [math]G[/math] .

    Алгоритм

    Наивный алгоритм

    Найдём центр графа исходя из его определения.

    • Построим матрицу [math]A_[/math] ( [math]n[/math] — мощность множества [math]V[/math] ), где [math]a_ = d_[/math] , то есть матрицу кратчайших путей. Для её построения можно воспользоваться алгоритмом Флойда-Уоршелла или Дейкстры.
    • Подсчитаем максимум в каждой строчке матрицы [math]A[/math] . Таким образом, получим массив длины [math]n[/math] .
    • Найдём наименьший элемент в этом массиве. Эта вершина и есть центр графа. В том случае, когда вершин несколько, все они являются центрами.

    Асимптотика зависит от используемого способа подсчета кратчайших путей. При Флойде это будет [math]O(V^3)[/math] , а при Дейкстре — максимум из асимптотики конкретной реализации Дейкстры и [math]O(V^2)[/math] , за которую мы находим максимумы в матрице.

    Алгоритм для дерева за O(n)

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

    • Пройдёмся по дереву обходом в глубину и пометим все висячие вершины числом [math]0[/math] .
    • Обрежем помеченные вершины.
    • Образовавшиеся листья пометим числом [math]1[/math] и тоже обрежем.
    • Будем повторять, пока на текущей глубине не окажется не более двух листьев, и при этом в дереве будет тоже не более двух листьев.

    Оставшиеся листья являются центром дерева.

    Для того, чтобы алгоритм работал за [math]O(n)[/math] , нужно обрабатывать листья по одному, поддерживая в очереди два последовательных по глубине слоя.

    См. также

    Источники информации

    Источник

    Читайте также:  Санитарная обрезка деревьев нормативы
Оцените статью