Деревья. Кратко напомним


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



Определение. Построение кода Прюфера для заданного дерева


Код Прюфера – это способ взаимно однозначного кодирования помеченных деревьев с n вершинами с помощью последовательности n-2 целых чисел в отрезке [1,n]. То есть, можно сказать, что код Прюфера – это биекция между всеми остовными деревьями полного графа и числовыми последовательностями.


Данный способ кодирования деревьев был предложен немецким математиком Хайнцом Прюфером в 1918 году.


Рассмотрим алгоритм построения кода Прюфера для заданного дерева с n вершинами.


На вход подается список ребер. Выбирается лист дерева с наименьшим номером, затем он удаляется из дерева, и к коду Прюфера добавляется номер вершины, которая была связана с этим листом. Эта процедура повторяется n-2 раза. В конце концов, в дереве останется только 2 вершины, и алгоритм на этом завершается. Номера оставшихся двух вершин в код не записываются.


Таким образом, код Прюфера для заданного дерева – это последовательность из n-2 чисел, где каждое число – номер вершины, связанной с наименьшим на тот момент листом – то есть это число в отрезке [1,n].


Пример
image
Исходное дерево
image
Код Прюфера: 1
image
Код Прюфера: 1 5
image
Код Прюфера: 1 5 2
image
Код Прюфера: 1 5 2 6
image
Код Прюфера: 1 5 2 6 6
image
Код Прюфера: 1 5 2 6 6 2
image
Код Прюфера: 1 5 2 6 6 2 1
image
Код Прюфера: 1 5 2 6 6 2 1 3



Восстановление дерева по его коду Прюфера


Рядом с задачей построения кода Прюфера стоит задача восстановления закодированного дерева. Будем рассматривать алгоритм восстановления дерева со следующими условиями: на вход подается последовательность цифр (вершин), которая представляет код Прюфера, результатом будет список ребер дерева. Таким образом, задача будет решена.


Рассмотрим алгоритм декодирования подробно. Помимо кода нам нужен список всех вершин графа. Мы знаем, что код Прюфера состоит из n-2 вершин, где n – это число вершин в графе. То есть, мы можем по размеру кода определить число вершин в закодированном дереве.


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


Пример
Восстановим дерево по коду Прюфера, который был получен в примере кодирования.


Первый шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 5 6 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 4
Список ребер: 1 4


Второй шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 5 6 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 7
Список ребер: 1 4, 5 7


Третий шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 5 6 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 5
Список ребер: 1 4, 5 7, 2 5


Четвертый шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 56 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 8
Список ребер: 1 4, 5 7, 2 5, 6 8


Пятый шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 56 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 9
Список ребер: 1 4, 5 7, 2 5, 6 8, 6 9


Шестой шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 56 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 6
Список ребер: 1 4, 5 7, 2 5, 6 8, 6 9, 2 6


Седьмой шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 5 6 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 2
Список ребер: 1 4, 5 7, 2 5, 6 8, 6 9, 2 6, 1 2


Восьмой шаг
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 5 6 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 1
Список ребер: 1 4, 5 7, 2 5, 6 8, 6 9, 2 6, 1 2, 3 1


Завершение алгоритма
Код Прюфера: 1 5 2 6 6 2 1 3
Массив вершин дерева: 1 2 3 4 5 6 7 8 9 10
Минимальная вершина, не содержащаяся в коде Прюфера – это 1
Список ребер: 1 4, 5 7, 2 5, 6 8, 6 9, 2 6, 1 2, 3 1, 3 10


Заключение


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


Источники


  1. Уилсон Р. Введение в теорию графов. Перевод с англ. М.: Мир, 1977. -208 с.
  2. Комбинаторика. Булевы функции. Графы: учеб. пособие / А. А. Балагура, О. В. Кузьмин. – Иркутск: Изд-во ИГУ, 2012. – 115 с.
  3. MAXimal [Электронный ресурс] — Режим доступа: ссылка, свободный. (дата обращения: 22.04.2017)
Поделиться с друзьями
-->

Комментарии (17)


  1. cl0ne
    28.06.2017 16:48
    +3

    Можно картинки не из ВК? Ибо не у всех отобразятся. Спасибо.


    1. LoadRunner
      29.06.2017 13:23

      А разве скрипт Хабра автоматически не перезаливает картинки со сторонних источников на hsto.org?


  1. ov7a
    28.06.2017 17:04
    +5

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

    Можно с этого места поподробнее?


    1. yeputons
      29.06.2017 01:33

      Могу предложить такой пример: равновероятно сгенерировать случайное дерево. Кожд Прюфера кажется самым простым решением, как без него добиться равновероятности деревьев — непонятно.


      1. ov7a
        29.06.2017 11:40
        +1

        Просто любое случайное дерево? Их же бесконечное количество?
        Или дерево определенного размера? Дерево с ограничением на размер сверху? Остовное дерево для заданного графа? k-ичное дерево?
        Это все очень разные задачи, уточните пожалуйста, какую именно вы имели в виду.


        1. yeputons
          29.06.2017 21:52

          Сгенерировать произвольное дерево на ровно N пронумерованных вершинах.


        1. yeputons
          29.06.2017 21:56

          При этом, стоит заметить, генерация дерева на не более чем N вершинах через генерацию на ровно N вершинах довольно легко выражается


          1. wataru
            29.06.2017 23:10

            И как это делается?


            1. yeputons
              29.06.2017 23:12

              Мы знаем количество деревьев на 1, 2, 3, ..., N вершинах. Выберем сначала с соответствующими вероятностями число вершин (т.е. выбираем число K с вероятностью, пропорциональной K^{K-2}), а потом сгенерируем дерево на ровно K вершинах.


      1. blockmak
        29.06.2017 15:02

        А можно для тех кто "не в теме", что значит " добиться равновероятности дерева"?


        1. DouTro
          29.06.2017 15:11

          То и значит. Каждое дерево имеет одинаковую вероятность с остальными быть выбранным из всего набора деревьев.


        1. yeputons
          29.06.2017 21:55

          Вот есть множество всех деревьев на N вершинах, их конечно, а точнее — N^{N-2} по теореме Кэли. Давайте их как-нибудь занумеруем, потом выберем случайное число от 1 до N^{N-2} равновероятно (т.е. каждое число выбирается с одинаковой вероятностью), возьмём дерево с таким номером.


  1. ukhegg
    28.06.2017 22:25
    +6

    Хотелось бы примеры использования данного алгоритма в выч. технике. А то в текущем виде, ИМХО, место этой статье где-нибудь на математических форумах, но не на хабре.


  1. pyJIoH
    29.06.2017 09:25
    +1

    Алгоритм выглядит чисто академическим, применимым в каком-то очень узком круге задач (я не нашел в каком). Но кому интересно могут порешать олимпиадную задачку на код Прюфера


    1. qadabr
      29.06.2017 15:02

      Хм, если я все правильно понял, то этот алгоритм хорошо сочетается с алгоритмом Хаффмана в том плане, что придется хранить как-то дерево таким образом, чтобы оно занимало как можно меньше памяти


  1. DouTro
    29.06.2017 15:02

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


  1. wataru
    29.06.2017 16:15
    +1

    В шаге про завершение алгоритма опечатка, там нет никакой минимальной вершины в не в коде. Тем более ей не может быть 1.


    Основной вопрос, если уж алгоритм обсуждать, а за какую сложность можно этот код построить? А как долго приведенный алгоритм восстановления дерева работает? Можно ли его за линейную сложность реализовать?