Copiar construtor para uma árvore binária C ++
-
20-09-2019 - |
Pergunta
Eu tenho uma aula de árvore com a seguinte definição:
class Tree {
Tree();
private:
TreeNode *rootPtr;
}
Treenode representa um nó e possui dados, leftptr e RightPtr.
Como faço para criar uma cópia de um objeto de árvore usando um construtor de cópia? Eu quero fazer algo como:
Tree obj1;
//insert nodes
Tree obj2(obj1); //without modifying obj1.
Qualquer ajuda é apreciada!
Solução
Pseudo-código:
struct Tree {
Tree(Tree const& other) {
for (each in other) {
insert(each);
}
}
void insert(T item);
};
Exemplo concreto (alterando a maneira como você anda na árvore é importante saber, mas prejudica a mostra como a cópia funciona e pode estar fazendo muito da lição de casa de alguém aqui):
#include <algorithm>
#include <iostream>
#include <vector>
template<class Type>
struct TreeNode {
Type data;
TreeNode* left;
TreeNode* right;
explicit
TreeNode(Type const& value=Type()) : data(value), left(0), right(0) {}
};
template<class Type>
struct Tree {
typedef TreeNode<Type> Node;
Tree() : root(0) {}
Tree(Tree const& other) : root(0) {
std::vector<Node const*> remaining;
Node const* cur = other.root;
while (cur) {
insert(cur->data);
if (cur->right) {
remaining.push_back(cur->right);
}
if (cur->left) {
cur = cur->left;
}
else if (remaining.empty()) {
break;
}
else {
cur = remaining.back();
remaining.pop_back();
}
}
}
~Tree() {
std::vector<Node*> remaining;
Node* cur = root;
while (cur) {
Node* left = cur->left;
if (cur->right) {
remaining.push_back(cur->right);
}
delete cur;
if (left) {
cur = left;
}
else if (remaining.empty()) {
break;
}
else {
cur = remaining.back();
remaining.pop_back();
}
}
}
void insert(Type const& value) {
// sub-optimal insert
Node* new_root = new Node(value);
new_root->left = root;
root = new_root;
}
// easier to include simple op= than either disallow it
// or be wrong by using the compiler-supplied one
void swap(Tree& other) { std::swap(root, other.root); }
Tree& operator=(Tree copy) { swap(copy); return *this; }
friend
ostream& operator<<(ostream& s, Tree const& t) {
std::vector<Node const*> remaining;
Node const* cur = t.root;
while (cur) {
s << cur->data << ' ';
if (cur->right) {
remaining.push_back(cur->right);
}
if (cur->left) {
cur = cur->left;
}
else if (remaining.empty()) {
break;
}
else {
cur = remaining.back();
remaining.pop_back();
}
}
return s;
}
private:
Node* root;
};
int main() {
using namespace std;
Tree<int> a;
a.insert(5);
a.insert(28);
a.insert(3);
a.insert(42);
cout << a << '\n';
Tree<int> b (a);
cout << b << '\n';
return 0;
}
Outras dicas
Depende se você deseja um raso ou profundo cópia de. Supondo uma cópia profunda, você precisa copiar o que quer que esteja nas "folhas" penduradas em um TreeNode
objeto; Então, idealmente, a funcionalidade deve estar em TreeNode
(a não ser que Tree
é uma aula de amizade de TreeNode
que você projetou para estar profundamente familiarizado com sua implementação, o que geralmente é o caso, é claro ;-). Supondo algo como ...:
template <class Leaf>
class TreeNode {
private:
bool isLeaf;
Leaf* leafValue;
TreeNode *leftPtr, *rightPtr;
TreeNode(const&Leaf leafValue);
TreeNode(const TreeNode *left, const TreeNode *right);
...
Então você pode adicionar a isso um
public:
TreeNode<Leaf>* clone() const {
if (isLeaf) return new TreeNode<Leaf>(*leafValue);
return new TreeNode<Leaf>(
leftPtr? leftPtr->clone() : NULL,
rightPtr? rightPtr->clone() : NULL,
);
}
Se Tree
está cuidando desse nível de funcionalidade (como uma aula de amizade); obviamente, você terá o equivalente exato, mas com o nó sendo clonado como um arg explícito.
Duas opções básicas:
Se você tiver um iterador disponível, simplesmente iterar sobre os elementos da árvore e inserir cada um manualmente, como R. Pate descreveu. Se a sua classe de árvores não tomar medidas explícitas para equilibrar a árvore (por exemplo, AVL ou rotações vermelhas-pretas), você acabará efetivamente com uma lista vinculada de nós dessa maneira (ou seja, todos os ponteiros da esquerda serão nulos ). Se você está equilibrando sua árvore, efetivamente fará o trabalho de equilíbrio duas vezes (já que já precisava descobrir na árvore de origem da qual está copiando).
Uma solução mais rápida, mas mais confusa e mais propensa a erros, seria construir a cópia para baixo, fazendo uma travessia de largura ou profundidade da estrutura da árvore de origem. Você não precisaria de rotações de equilíbrio e acabaria com uma topologia idêntica de nós.
Aqui está outro exemplo que usei com uma árvore binária. Neste exemplo, o nó e a árvore são definidos em classes separadas e um copyHelper
função recursiva ajuda o copyTree
função. O código não está concluído, tentei colocar apenas o que era necessário para entender como as funções são implementadas.
CopyHelper:
void copyHelper( BinTreeNode<T>* copy, BinTreeNode<T>* originalNode ) {
if (originalTree == NULL)
copy = NULL;
else {
// set value of copy to that of originalTree
copy->setValue( originalTree->getValue() );
if ( originalTree->hasLeft() ) {
// call the copyHelper function on a newly created left child and set the pointers
// accordingly, I did this using an 'addLeftChild( node, value )' function, which creates
// a new node in memory, sets the left, right child, and returns that node. Notice
// I call the addLeftChild function within the recursive call to copyHelper.
copyHelper(addLeftChild( copy, originalTree->getValue()), originalTree->getLeftChild());
}
if ( originalTree->hasRight() ) { // same with left child
copyHelper(addRightChild(copy, originalTree->getValue()), originalTree->getRightChild());
}
} // end else
} // end copyHelper
cópia de: retorna um ponteiro para a nova árvore
Tree* copy( Tree* old ) {
Tree* tree = new Tree();
copyHelper( tree->root, oldTree->getRoot() );
// we just created a newly allocated tree copy of oldTree!
return tree;
} // end copy
Uso:
Tree obj2 = obj2->copy(obj1);
Espero que isso ajude alguém.
Quando sua classe tem um ponteiro apontando para a memória alocada dinamicamente, no construtor de cópias dessa classe, você precisa alocar memória para o objeto recém -criado. Então você precisa inicializar a memória recém -alocada com qualquer outro ponteiro apontando. Aqui está um exemplo de como você precisa lidar com uma classe com memória alocada dinamicamente:
class A
{
int *a;
public:
A(): a(new int) {*a = 0;}
A(const A& obj): a(new int)
{
*a = *(obj.a);
}
~A() {delete a;}
int get() const {return *a;}
void set(int x) {*a = x;}
};
Você pode tentar algo como (não testado)
class Tree {
TreeNode *rootPtr;
TreeNode* makeTree(Treenode*);
TreeNode* newNode(TreeNode* p)
{
TreeNode* node = new Treenode ;
node->data = p->data ;
node->left = 0 ;
node->right = 0 ;
}
public:
Tree(){}
Tree(const Tree& other)
{
rootPtr = makeTree(other.rootPtr) ;
}
~Tree(){//delete nodes}
};
TreeNode* Tree::makeTree(Treenode *p)
{
if( !p )
{
TreeNode* pBase = newNode(p); //create a new node with same data as p
pBase->left = makeTree(p->left->data);
pBase->right = makeTree(p->right->data);
return pBase ;
}
return 0 ;
}