Прежде всего, есть пара дополнительных сведений, которые могут понадобиться.А именно:
- Какая разрешенная форма дерева?Судя по данному классу, это двоичное дерево, в котором каждому узлу разрешено иметь 0, 1 или 2 дочерних узла, поэтому я пойду с этим предположением.
- Что представляет собой «структурно отличающееся дерево»?Являются ли два дерева, которые являются зеркальным отражением друг друга, структурно различными?Я предполагаю, что два дерева структурно различны, если они не полностью идентичны.
С учетом этого наилучшим подходом было бы построить каждое дерево размером i
узлов с использованием некоторой глубины.-первый или широкий-первый алгоритм.Возьмем, к примеру, все формы дерева, которое имеет 4 узла.Если вы идете вглубь, это означает, что вы сначала исследуете все формы, в которых корневой узел имеет только левого потомка.Затем вы будете продолжать в том же духе, пока не дойдете до трех узлов.Я собираюсь показать это в простом ASCII, так как мне лень запускать графическую программу.Изображение каждого слэша соединяет два невидимых узла:
1: /
/
/
Таким образом, у нас слева направо.Следующий вариант получается путем изменения этого последнего решения, приводя к лево-лево-правому:
2: /
/
\
Следующим вариантом будет иметь как левый, так и правый дочерний узел в конце, но так как это будетдайте нам дерево размером 5, мы не можем этого сделать.Мы исчерпали наши возможности, поэтому вернемся на один шаг к предпоследнему узлу.Изначально мы выбрали левую для этого, так что теперь мы идем с правой и продолжим алгоритм оттуда:
3: / 4: /
\ \
/ \
Видите, что мы сделали?Мы сделали другой выбор для второго узла, а затем исследовали все остальные варианты оттуда.Как и в 1 и 2.
Хорошо, варианты снова исчерпаны, поэтому мы должны вернуться на шаг назад.Мы исследовали влево и вправо для второго узла.На этот раз остался еще один вариант: два дочерних узла на втором уровне:
5: /
/\
Прежде чем продолжить чтение, попробуйте следовать этой логике и нарисуйте следующие шаги, поскольку вы считаете, что они развернутся.Запомните логику: создайте левый узел и создайте все поддеревья, сделайте правый узел и создайте все поддеревья, создайте оба (если это не превышает предел) и создайте все поддеревья.Как только все опции исчерпаны, вернитесь на шаг и измените их.
Вот остаток:
6: \ 7: \ 8: \ 9: \ 10: \ 11: /\ 12: /\ 13: /\ 14: /\
/ / \ \ /\ / \ / \
/ \ / \
14 форм.Слава Богу, все получилось.
Имейте в виду, что это алгоритм первой глубины.Мы углубляемся, пока не достигнем предела нашего узла, затем вернемся на один шаг назад и сделаем другой выбор, и так далее, пока эти опции не будут исчерпаны.Тогда мы вернемся еще на один шаг.Если бы вы использовали ширину в первую очередь, вы всегда возвращались бы как можно дальше назад и выбирали другой вариант.Я покажу вам первые три шага в ширину:
1: / 2: \ 3: /\
/ / /
/ /
Просто рассмотрите образец в ширину как информационный.Также обратите внимание, что мой первый порядок глубины «влево, вправо, оба» несколько произвольный.С таким же успехом вы могли бы использовать «правый, левый, оба».
Итак, как вы на самом деле превращаете это в код?Что ж, понаблюдайте, как после того, как вы приняли решение (слева, справа, оба), оттуда расширяются остальные формы дерева.Можно сказать, что вы снова столкнулись с той же проблемой, но теперь для дерева меньшего размера.
Вернитесь к первому шагу глубины 1. Подумайте, как оно было построено.У нас есть корневой узел.Это один из способов, так что 3 узла еще предстоит выделить.Оттуда мы должны сделать выбор.Наш первоначальный выбор был сделать только левым ребенком.Это означает, что мы можем на данный момент игнорировать правильное поддерево.Начиная с этого дочернего элемента, теперь у нас есть проблема построения всех деревьев с 3 узлами, корневым узлом которых является этот дочерний элемент.Еще раз, мы выбираем вариант добавления только левого потомка.И это подводит нас к проблеме построения всех деревьев размера 2.
Это известно как рекурсивная проблема.Чтобы найти решение для i
, мы должны знать решение для i-1
.Чтобы остановить рекурсию, должна быть какая-то основа, которая не опирается на более ранние результаты.В нашем случае это i=1
, что является тривиальным решением: только один узел.
Таким образом, это означает, что вы можете написать фрагмент кода, который найдет все формы дерева размером i
, используя себя с параметромi-1
.Чтобы избежать бесконечного цикла, остановите рекурсию на i=1
.Чтобы проверить размер вашего текущего дерева, чтобы не нарушить ограничение узла, вы можете использовать тот метод size()
, который у вас есть.Обратите внимание, что сам по себе это рекурсивный метод: он использует себя для вычисления результата.Другие методы также могут быть полезны, но я не думаю, что вам понадобятся все из них.
Независимо от того, выберете ли вы глубину или ширину, решать вам.Попробуйте подумать о том, как они будут выражаться в виде алгоритмов и требований к процессу программирования.Проще ли реализовать в глубину, чем в ширину?
Надеюсь, моя чепуха в ASCII была несколько понятной, и я не выгляжу слишком снисходительно.Я не знаю, как далеко вы продвинулись с этой проблемой или какие уроки вы изучаете.Деревья - это общая структура данных, а ходьба деревьев по глубине или ширине (например, в алгоритме поиска) является распространенным алгоритмом, поэтому ожидайте увидеть больше из этого, если ИТ - ваша основная задача.Видя, как закончилось задание, но вы все еще хотите найти решение, я уверен, что вы хорошо усвоите.
Дайте нам знать, узнаете ли вы, как это сделать в коде.Если у вас все еще есть проблемы, я постараюсь опубликовать (возможно частичное) решение через некоторое время.