Я внедряю AVLTree в C ++ как упражнение для подготовки будущих проектов. Дерево AVLTree в основном представляет собой дерево BSTTree, но с дополнительной детализацией, что дерево сбалансировано, т. Е. Для узла x с левым дочерним элементом y и правым дочерним элементом z число дочерних элементов слева от x (дочерних элементов y и дочерних элементов y) не может отличаются от числа дочерних элементов справа от x более чем на 1 узел.
Каждый узел будет представлять значение типа int и имеет ссылку на родительский узел, на возможно существующий левый дочерний узел и на возможно существующий правый дочерний узел.
class BSTNode {
protected:
int value;
BSTNode* parent;
BSTNode* left;
BSTNode* right;
...
public:
virtual void setLeftChild(BSTNode* left);
...
}
Чтобы отследить количество дочерних узлов, у меня есть AVLNode, расширяющий BSTNode двумя целыми числами, leftAffinity и rightAffinity, где leftAffinity сообщает мне количество узлов слева ( аналогично для права с rightAffinity). Поскольку я не гарантирую, что значения, которые я добавляю, всегда уникальны, я не могу обновить сходство, пока не найду место для размещения нового узла.
class AVLNode : public BSTNode {
private:
int leftAffinity;
int rightAffinity;
...
public:
void setLeftChild(BSTNode* left) override;
...
}
Как только я успешно установил левый потомок узла равным left , в AVLNode я также обновляю leftAffinity
текущего узла (и рекурсивно до родительского элемента родительского элемента до достижения root дерева) до left->getLeftAffinity() + left->getRightAffinity()
.
Проблема здесь в том, что функции по схожести определены в AVLNode, поэтому я не могу немедленно вызвать left-> getLeftAffinity () без приведения, так как здесь я не знаю, является ли left
BSTNode или AVLNode. Я знаю, что идея заключается в том, чтобы любой дочерний узел AVLNode также был только AVLNode, что можно было бы обеспечить, гарантируя, что любой BSTNode, который не является AVLNode, преобразуется в AVLNode.
- Я делаю я не хочу изменять аргумент функции для получения AVLNode * вместо этого, так как это вынуждает меня объявлять left, right и parent как AVLNode * в классе AVLNode, и, таким образом, я получаю дубликаты переменных, по одной для каждого класса BSTNode и по одному для AVLNode класс, даже если left, right и parent являются частными в классе BSTNode.
- Я не хочу использовать динамическое приведение c, поскольку целью упражнения было создание эффективной древовидной структуры данных с
O(log n)
сложность операций вставки и получения. Я читал, что Dynami c литье и дорого, и его также следует избегать, так как оно обычно намекает на недостаток дизайна.
Возможные подходы:
- Создать " no-op "функция в классе BSTNode, которая переопределяется в классе AVLNode для управления сходствами узлов. Эта функция не должна быть там, так как она не связана с BSTNode.
- Dynami c приведение к каждой операции добавления, и при успешном завершении, один раз для каждого родителя до root дерева, что слишком дорогой.
- Не использовать виртуальный вообще для этих функций, что заставило бы меня перегрузить многие функции в AVLNode.
- Измените все функции, чтобы получать AVLNode * вместо BSTNode *, в то же время заставляя преобразование BSTNode * в AVLNode * с конструктором в AVLNode, но это потребует динамического приведения c, так как в противном случае я потеряю leftAffinity и rightAffinity добавляемого узла и вызову другие проблемы с наследованием функции.
Выбор, который мне кажется лучшим, заключается в том, чтобы использовать подход «без операции», так как он работал бы, если бы производительность была ключевой.
Я новичок в C ++, поэтому я ценю любые мысли о подходах, которые я использую. я написал и, например, если есть тривиальное решение, которое я пропустил. Что будет лучшим выбором в этой ситуации?