восстановление дерева из его списков предварительного и последующего порядков

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

Я считаю, что можно восстановить дерево именно из этих двух списков, и я думаю, что у меня есть алгоритм для этого, но я не доказал это. Поскольку это будет частью магистерского проекта, мне нужно быть абсолютно уверенным, что это возможно и правильно (математически доказано). Однако это не будет целью проекта, поэтому мне было интересно, есть ли там источник (например, бумага или книга), который я мог бы процитировать в качестве доказательства. (Может быть, в TAOCP? Возможно, кто-нибудь знает раздел?)

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


Примечание: рассматриваемое дерево, вероятно, не будет двоичным, сбалансированным или чем-то еще, что могло бы сделать его слишком простым.

Примечание 2: было бы еще лучше использовать только предварительный заказ или список после заказа, но я не думаю, что это возможно.

Примечание 3: узел может иметь любое количество дочерних элементов.

Примечание 4: меня волнует только порядок братьев и сестер. Левый или правый не имеет значения, если есть только один ребенок.


person NomeN    schedule 16.07.2009    source источник
comment
Я почти уверен, что это возможно с inorder и preorder / postorder, но я не думаю, что это возможно с preorder и postorder. Я уверен, что с одним списком это невозможно.   -  person mmx    schedule 16.07.2009
comment
Узлы должны быть уникальными, верно? Потому что иначе вы не сможете различить, например (a, [(a, [(a, [])]), (a, [])]) и (a, [(a, []), (a, [(a, [])])]).   -  person Stephan202    schedule 16.07.2009
comment
Я думаю, вам нужно уточнить, что вы говорите о деревьях, где у узла может быть произвольное количество потомков. Часто деревья определяются так, что каждый узел имеет ровно два дочерних элемента, и один или оба из них могут быть пустыми. В этом случае невозможно восстановить дерево по предварительному и последующему порядку, потому что вы не можете определить, является ли один дочерний элемент левым или правым (см. Обсуждение здесь: profile.iiita.ac.in/pkmallick_03/pages/3_16.html).   -  person Martin B    schedule 16.07.2009
comment
@Mehrdad Кажется, я читал, что порядок не имеет смысла для небинарных деревьев.   -  person NomeN    schedule 16.07.2009
comment
@Stephan, конечно, узлы уникальны.   -  person NomeN    schedule 16.07.2009
comment
@Martin B Действительно, узлы в деревьях, с которыми я работаю, имеют произвольное количество потомков   -  person NomeN    schedule 16.07.2009
comment
Предлагаемое мной решение работает с произвольным количеством дочерних элементов и должным образом сохраняет их порядок.   -  person AlbertoPL    schedule 16.07.2009
comment
Кстати, я думаю, что это можно доказать с помощью контрпримера. Предположим, что описанная техника НЕ ​​работает, и обнаружим противоречие. На самом деле это всего лишь инстинкт, но я думаю, что это правильный путь.   -  person AlbertoPL    schedule 16.07.2009
comment
Чтобы уточнить, доказательство контрпримером в лучшем случае дает вам доказательство существования, а не реальный алгоритм. Проблема с предположением, что это не работает, заключается в том, что вы должны предполагать любые комбинации причин, по которым это не работает.   -  person MSalters    schedule 16.07.2009
comment
Это был вопрос моего собеседования в Google;)   -  person JohnJohnGa    schedule 30.12.2011


Ответы (7)


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

Вот моя попытка решения:

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

Обход вашего почтового заказа может определить глубину дерева. Например, допустим, у меня есть такая структура:

      1
  2   5   6
 3 4  7

Where 2 is the parent of 3 and 4, and 5 is the parent of 7.

Preorder: 1 2 3 4 5 7 6
Postorder: 3 4 2 7 5 6 1

Мы знаем, что начинаем с 1, потому что это первый узел в обходе предварительного заказа. Затем мы смотрим на следующее число, 2. В почтовом порядке, поскольку число 2 идет ПЕРЕД узлом 1, мы знаем, что 2 должен быть дочерним по отношению к 1. Затем мы смотрим на 3. 3 идет перед 2, и, следовательно, 3 является потомком 2. 4 - это потомок 2, но после 3, поэтому мы знаем, что 4 - это потомок 2, но НЕ ребенок 3. И т. д.

Теперь это может не сработать, если узлы не уникальны, но, по крайней мере, это начало решения.

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

Edit2: Доказательство может лежать здесь: http://ieeexplore.ieee.org/Xplore/login.jsp?url=http%3A%2F%2Fieeexplore.ieee.org%2Fiel2%2F215%2F626%2F00017225.pdf%3Farnumber%3D17225&authDecision=-203

Думаю, вам необходимо приобрести документ, однако ...

Вот письменное доказательство решения:

http://www14.informatik.tu-muenchen.de/lehre/2007WS/fa-cse/tutorials/tutorial09-solutions.pdf

person AlbertoPL    schedule 16.07.2009
comment
Мое решение очень похоже, однако я ищу проверенный алгоритм. Если его нет, я могу попытаться доказать свой алгоритм с помощью SO. Если даже все сообщество SO не может доказать, что это неправильно, оно должно быть правым ... верно? - person NomeN; 16.07.2009
comment
Если все сообщество SO не может доказать, что это неправильно, тогда это МОЖЕТ быть правильным: P - person AlbertoPL; 16.07.2009
comment
Я говорю кодируйте это и пробуйте это с как можно большим количеством деревьев, однако я считаю, что это математически корректно, я просто не умею начинать доказательства с нуля. - person AlbertoPL; 16.07.2009
comment
Я тоже, поэтому вопрос ... и это чертовски экономит время, даже если я умел писать доказательства. - person NomeN; 16.07.2009
comment
Я нашел документ, в котором два алгоритма, использующие обход дерева до и после заказа, восстанавливают дерево. Однако за это нужно платить ... - person AlbertoPL; 16.07.2009
comment
Также проверьте второй источник, который я только что добавил, это письменное решение проблемы. - person AlbertoPL; 16.07.2009
comment
Боюсь, что оба источника относятся к двоичным деревьям. Я не уверен, что его можно обобщить для деревьев с произвольным числом детей. - person NomeN; 17.07.2009
comment
Хотя это не то, что вам нужно, вот бесплатная версия первой статьи - ntur.lib.ntu.edu.tw/bitstream/246246/2007041910032125/1/ - person Richard Dunlap; 17.07.2009
comment
К счастью, поскольку это проект для университета в исследовательском учреждении, у меня есть два способа получить статью бесплатно. У обоих есть подписка на все документы IEEE. Но для всех, кто просматривает это ... хорошая работа Альберто и Ричард в поиске бесплатных источников. - person NomeN; 17.07.2009
comment
этот подход также называется проверкой с предварительным условием. Основы алгоритмики из главы 9.2 Брассара. - person systemsfault; 23.04.2012

Предварительный и последующий порядок не определяют дерево однозначно.

В общем, обход одного дерева не определяет однозначно структуру дерева. Например, как мы видели, для обоих следующих деревьев обход по порядку дает [1,2,3,4,5,6].

    4                     3
   / \                   / \
  2   5                 2   5
 / \   \               /   / \
1   3   6             1   4   6

Такая же неоднозначность присутствует для обходов до и после заказа. Предварительный обход для первого дерева выше [4,2,1,3,5,6]. Вот другое дерево с таким же обходом предварительного заказа.

    4
   / \
  2   1
     / \
    3   6
     \
      5

Точно так же мы можем легко построить другое дерево, чей обход по порядку [1,3,2,6,5,4] совпадает с обходом первого дерева выше.

person Bill the Lizard    schedule 16.07.2009
comment
Ах ... но меня не интересуют левые или правые дети, а только порядок братьев и сестер. Отредактирую свой вопрос. +1 за источник хотя. - person NomeN; 16.07.2009
comment
Верно, что эти обходы не идентифицируют дерево однозначно. ОДНАКО, если вы включаете нулевые / листовые узлы, то порядок обхода ДЕЙСТВИТЕЛЬНО уникально идентифицирует дерево. - person Captain_Obvious; 14.12.2019

Рассмотрим произвольное дерево T как четверку (A, B, C, D), где A - корневой узел, B - корень. узел первого дочернего элемента, C - это вектор любых непустых дочерних элементов B, а D - вектор любых непустых дочерних элементов B. C и D сами по себе являются деревьями.

Любой из A, B, C и D может быть пустым. Если B пусто, то должны быть C и D; если А, то все.

Поскольку узлы уникальны, наборы узлов, содержащиеся где-либо в пределах C и D, не пересекаются, и ни один из них не содержит A или B.

Функции pre () и post () генерируют упорядоченные последовательности формы:

pre (T) = [A, B, pre (C), pre (D) ]

сообщение (T) = [сообщение (C), B, сообщение (D) , A]

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

Теперь рассмотрим случаи:

  • если A пусто, выходом обеих функций будет пустая последовательность []
  • если B пусто, результат обеих функций будет просто [A]
  • если C и D пусты, pre (T) = [A, B] и post (T) = [B, A]
  • если пусто только C, pre (T) = [A, B, D '] и post (T) = [B, D '', A] (где штрихи указывают на некоторую перестановку узлов, содержащихся в D)
  • если пусто только D, pre (T) = [A, B, C '] и post (T) = [C '', B, A]
  • если ни один из них не пуст, pre (T) = [A, B, C ', D'] и post (T) = [C '', B, D '', A]

Во всех случаях мы можем однозначно разделить элементы двух выходных последовательностей на соответствующие подпоследовательности, используя A и B (если они есть) в качестве разделителей.

Тогда возникает вопрос, можем ли мы также разделить векторные последовательности? Если мы можем, то каждый может быть рекурсивно обработан, и все готово.

Поскольку результатом pre () всегда будет цепочка последовательностей, начинающихся с узлов A, а результатом post () всегда будет цепочку последовательностей, заканчивающихся узлами A, мы действительно можем разделить их, при условии, что узлы A никогда не бывают пустыми.

Это то место, где процесс падает в случае двоичных (или даже любых) деревьев с фиксированными дочерними элементами, которые могут независимо быть пустыми. В нашем случае, однако, мы определили C и D только непустые узлы, поэтому реконструкция гарантированно работает.

Во всяком случае, я так думаю. Очевидно, это всего лишь аргумент, а не формальное доказательство!

person walkytalky    schedule 22.04.2010
comment
Я полагаю, что вы придумали тот же алгоритм, что и я, в конце концов я превратил его в небольшую статью, потому что не смог найти ни одного цитируемого ресурса. Основная идея состоит в том, что вы можете рекурсивно реконструировать дерево из-за положения корня (под) дерева в pre и post соответственно, все дочерние элементы будут находиться между этими позициями. Тогда первый дочерний элемент - это крайний левый из этих узлов в pre, второй - крайний левый узел, который не является дочерним по отношению к первому и т. Д. - person NomeN; 22.04.2010
comment
И ваши усилия очень ценятся, но я должен сказать ... некромантия? (+1 в любом случае) - person NomeN; 22.04.2010
comment
Просто так случилось, что я оказался на первой полосе, когда я зашел (из-за того, что komal не ответил, я думаю) и вызвал у меня интерес. Как насчет того, чтобы разместить ссылку на свою статью для дальнейшего использования? - person walkytalky; 23.04.2010

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

Теперь напишите его списки Preorder и Postorder. затем попробуйте восстановить дерево из этих списков. и вы понимаете, что на этом узле вы не можете решить, правый или левый его дочерний элемент.

person Maryam A    schedule 30.12.2011
comment
Во-первых, довольно круто, что спустя все это время все еще есть дополнения! Для двоичного дерева алгоритм может быть скопирован из TAOCP, однако данное дерево не будет двоичным (или не будет ;-)), поэтому я боюсь, что эта проблема не подходит. - person NomeN; 04.01.2012
comment
хорошо, но вы можете обобщить мой ответ на любое другое дерево (не особенно на двоичное дерево, двоичное дерево - это самое простое дерево, которое вы можете сделать с инструкцией по этому поводу), Попробуйте! Я знаю, что ты можешь! ;-)))) - person Maryam A; 10.01.2012
comment
В Примечании 4 спрашивающий упоминает, что его не волнует левое-правое в случае только одного ребенка. - person danfuzz; 03.11.2012

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

X является предком Y тогда и только тогда, когда X предшествует Y в предварительном порядке и находится после Y в последующем.

Учитывая это, мы всегда можем найти всех потомков любого узла. Потомки X всегда следуют сразу за X в предварительном порядке и предшествуют X в последующем. Итак, как только мы узнаем, что заинтересованы в создании поддерева с корнем в X, мы можем извлечь предварительный и последующий обход для поддерева с корнем в X. Это естественным образом приводит к рекурсивному алгоритму, когда мы понимаем, что узел сразу после X должен быть его самый левый дочерний элемент, если он вообще является потомком.

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

person user2864574    schedule 09.10.2013

Как уже указывалось другими, двоичное дерево не может быть восстановлено с использованием только обхода до и после заказа. Единственный дочерний узел имеет неоднозначные обходы, которые не могут определить, является ли он левым или правым дочерним узлом, например. рассмотрите следующие обходы перед порядком и после заказа: предварительный заказ: a, b последующий порядок b, a

Он может производить оба следующих дерева

a a \ / b b Просто невозможно узнать, является ли b левым или правым потомком a, без какой-либо дополнительной информации, такой как обход порядка.

person shyamala    schedule 02.11.2015
comment
В данном случае меня не волновал левый или правый ребенок, достаточно было знать, что это единственный ребенок. Итак, да, я согласен, что обход технически неоднозначен, но практически не имеет значения для формы дерева, если один дочерний элемент будет просто представлен как прямая линия вниз - person NomeN; 02.11.2015

Невозможно построить общее двоичное дерево из предварительных и последующих обходов (см. Это). Но если мы знаем, что двоичное дерево заполнено, мы можем построить дерево без двусмысленности. Давайте разберемся в этом с помощью следующего примера.

Давайте рассмотрим два заданных массива как pre [] = {1, 2, 4, 8, 9, 5, 3, 6, 7} и post [] = {8, 9, 4, 5, 2, 6, 7 , 3, 1}; В pre [] крайний левый элемент является корнем дерева. Поскольку дерево заполнено, а размер массива больше 1. Значение рядом с 1 в pre [] должно быть левым потомком root. Итак, мы знаем, что 1 - корень, а 2 - левый потомок. Как найти все узлы в левом поддереве? Мы знаем, что 2 - это корень всех узлов в левом поддереве. Все узлы перед 2 в post [] должны находиться в левом поддереве. Теперь мы знаем, что 1 является корневым, элементы {8, 9, 4, 5, 2} находятся в левом поддереве, а элементы {6, 7, 3} находятся в правом поддереве.

              1
            /   \
           /      \
 {8, 9, 4, 5, 2}     {6, 7, 3}

Мы рекурсивно следуем описанному выше подходу и получаем следующее дерево.

      1
    /   \
  2       3
/  \     /  \

4 5 6 7 / \
8 9

person user4772217    schedule 10.04.2015
comment
Вы говорите "Посмотрите это", но там нет ссылки. - person dfeuer; 24.09.2015
comment
Достаточно ли порядка для восстановления полного двоичного дерева: со всеми заполненными уровнями, кроме последнего, начинающегося слева? - person Drimades Boy; 12.10.2019