views:

1357

answers:

5

I have a Tree class with the following definition:

class Tree {
  Tree();
private:
  TreeNode *rootPtr;
}

TreeNode represents a node and has data, leftPtr and rightPtr.

How do I create a copy of a tree object using a copy constructor? I want to do something like:

Tree obj1;
//insert nodes

Tree obj2(obj1); //without modifying obj1.

Any help is appreciated!

+7  A: 

Pseudo-code:

struct Tree {
  Tree(Tree const& other) {
    for (each in other) {
      insert(each);
    }
  }

  void insert(T item);
};

Concrete example (changing how you walk the tree is important to know, but detracts from showing how the copy ctor works, and might be doing too much of someone's homework here):

#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;
}
Roger Pate
I like this solution. But the issue I am having is that the rootPtr is a private data member of the Tree class. Is it ok to have a get function which returns the rootPtr?Without the rootPtr I dont know how to traverse the tree that is passed
AgentHunt
@Agent... If rootPtr is a member of Tree and the copy constructor is defined in Tree, then you can access the source tree's rootPtr from the destination tree... Remember, any object can access the privates of an object of the same type!
Polaris878
Are you saying , in the above example it is valid to access the rootPtr through the object "other"?I thought you could access the private members of the object you are working on(*this).
AgentHunt
See http://www.parashift.com/c++-faq-lite/basics-of-inheritance.html#faq-19.5: "A member declared in a private section of a class can only be accessed by member functions and friends of that class" <- of the class, not of that object.
Bill
Thank you!That worked like a charm
AgentHunt
+3  A: 

It depends on whether you want a shallow or deep copy. Assuming a deep copy, you need to be able to copy whatever's at the "leaves" hanging off a TreeNode object; so ideally the functionality should be in TreeNode (unless Tree is a friend class of TreeNode that you've designed to be deeply familiar with its implementation, which is often the case of course;-). Assuming something like...:

template <class Leaf>
class TreeNode {
  private:
    bool isLeaf;
    Leaf* leafValue;
    TreeNode *leftPtr, *rightPtr;
    TreeNode(const&Leaf leafValue);
    TreeNode(const TreeNode *left, const TreeNode *right);
  ...

then you could add to it a

  public:
    TreeNode<Leaf>* clone() const {
      if (isLeaf) return new TreeNode<Leaf>(*leafValue);
      return new TreeNode<Leaf>(
        leftPtr? leftPtr->clone() : NULL,
        rightPtr? rightPtr->clone() : NULL,
      );
    }

If Tree is taking care of this level of functionality (as a friend class), then obviously you'll have the exact equivalent but with the node being cloned as an explicit arg.

Alex Martelli
I think its safe to assume this person wants a deep copy... however it is good to point out the distinction.
Polaris878
+2  A: 

Two basic options:

If you have an iterator available, you can simply iterate over the elements in the tree and insert each one manually, as R. Pate described. If your tree class doesn't take explicit measures to balance the tree (e.g. AVL or red-black rotations), you'll end up effectively with a linked list of nodes this way (that is, all the left child pointers will be null). If you are balancing your tree, you'll effectively do the balancing work twice (since you already had to figure it out on the source tree from which you're copying).

A quicker but messier and more error-prone solution would be to build the copy top down by doing a breadth-first or depth-first traversal of the source tree structure. You wouldn't need any balancing rotations and you'd end up with an identical node topology.

Drew Hall
The first option is probably safer however more costly to construct O(N log(N)) while the second option is merely O(N)
Polaris878
Even without a specific iterator type in the public interface, it shouldn't be hard for him to walk the tree however he likes (even recursively).
Roger Pate
@R. Pate: Agreed--I was just trying to encourage him not to do an in-order traversal due to the unbalancing effect it would have.
Drew Hall
A: 

When your class has a pointer pointing to dynamically allocated memory, in the copy constructor of that class you need to allocate memory for newly created object. Then you need to initialize newly allocated memory with whatever the other pointer pointing at. Here is an example how you need to deal with a class having dynamically allocated memory:

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;}
};
Donotalo
A: 
Neeraj
I like this solution. But the issue I am having is that the rootPtr is a private data member of the Tree class. Is it ok to have a get function which returns the rootPtr?Without the rootPtr I dont know how to traverse the tree that is passed
AgentHunt
@Agent, check my comment on R.Pate's answer
Polaris878