It depends what you are wanting to do with it really - as well as possibly refactoring it to templatize the order, or separating the three traversal types, you might want to turn it into an internal iteration, allowing anything to visit the data in the tree:
enum order_t { PREORDER, INORDER, POSTORDER };
template<typename T, order_t order, typename F>
void traverse_binary_tree(BinaryTreeNode<T>* root, F f)
{
if( root == NULL ) return;
if(order == PREORDER) f(root->data);
traverse_binary_tree<T,order>(root->left,f);
if(order == INORDER) f(root->data);
traverse_binary_tree<T,order>(root->right,f);
if(order == POSTORDER) f(root->data);
}
template<typename T, typename F>
void traverse_binary_tree(BinaryTreeNode<T>* root, F f, order_t order = PREORDER)
{
switch (order) {
case PREORDER:
traverse_binary_tree<T,PREORDER>(root,f);
break;
case POSTORDER:
traverse_binary_tree<T,POSTORDER>(root,f);
break;
case INORDER:
traverse_binary_tree<T,INORDER>(root,f);
break;
}
}
(you may also want const F& and F& versions rather than the simple copying, pass-by-value function parameter, which would let you pass in functors which are mutable and produce a result; by-value is fine as long as your functor has no member variables or constructor)
Or you could create iterators which represent the three traversals, allowing you to write the output using std::copy; however, this would be a lot more code and wouldn't be done just for that purpose. However, creating an iterator would allow large trees to be processed without stack overflow, as you would have to either have a 'parent' pointer in each node, or have the iterator maintain an explicit stack of visited nodes.
Although the function itself becomes very simple:
std::ostream_iterator<std::string>(std::cout, " ");
std::copy(d.begin(order), d.end(), out);
Using an iterator doesn't simplify the implementation, either in terms of loc or actually being able to follow what is going on:
#include<string>
#include<iostream>
#include<functional>
#include<algorithm>
#include<iterator>
#include<vector>
#include<stack>
enum order_t { PREORDER, INORDER, POSTORDER };
template <typename T>
class BinaryTreeNode {
public:
BinaryTreeNode(const T& data) : data(data), left(0), right(0) { }
BinaryTreeNode(const T& data, BinaryTreeNode<T>* left, BinaryTreeNode<T>* right) : data(data), left(left), right(right) { }
public:
BinaryTreeNode<T>* left;
BinaryTreeNode<T>* right;
T data;
class BinaryTreeIterator {
BinaryTreeNode <T>* current;
std::stack<BinaryTreeNode <T>*> stack;
order_t order;
bool descending;
public:
typedef T value_type;
typedef std::input_iterator_tag iterator_category;
typedef void difference_type;
typedef BinaryTreeIterator* pointer;
typedef BinaryTreeIterator& reference;
BinaryTreeIterator (BinaryTreeNode <T>* current, order_t order) : current(current), order(order), descending(true)
{
if (order != PREORDER)
descend();
}
BinaryTreeIterator () : current(0), order(PREORDER), descending(false)
{
}
private:
void descend() {
do {
if (current->left) {
stack.push(current);
current = current -> left;
descending = true;
} else if ((order!=INORDER) && current->right) {
stack.push(current);
current = current -> right;
descending = true;
} else {
descending = false;
break;
}
} while (order != PREORDER);
}
public:
bool operator== (const BinaryTreeIterator& other) {
if (current)
return current == other.current && order == other.order;
else
return other.current == 0;
}
bool operator!= (const BinaryTreeIterator& other) {
return !((*this)==other);
}
const T& operator * () const {
return current -> data;
}
BinaryTreeIterator& operator++ () {
// if the stack is empty, then go to the left then right
// if the descending flag is set, then try to descending first
if (descending)
descend();
// not descending - either there are no children, or we are already going up
// if the stack is not empty, then this node's parent is the top of the stack
// so go right if this is the left child, and up if this is the right child
if (!descending) {
do {
if (stack.size()) {
BinaryTreeNode <T>* parent = stack.top();
// for inorder traversal, return the parent if coming from the left
if ((order == INORDER) && (current == parent->left)) {
current = parent;
break;
}
// if this is the left child and there is a right child, descending into the right
// or if this is the parent (inorder)
if ((current == parent) && (parent -> right)) {
current = parent->right;
descend();
// simulate return from descent into left if only the right child exists
if ((current->left == 0)&&(current->right))
stack.push(current);
break;
}
// if this is the left child and there is a right child, descending into the right
if ((current == parent->left) && (parent -> right)) {
descending = true;
current = parent->right;
if (order != PREORDER)
descend();
break;
}
// either has come up from the right, or there is no right, so go up
stack.pop();
current = parent;
} else {
// no more stack; quit
current = 0;
break;
}
} while (order != POSTORDER);
}
return *this;
}
};
BinaryTreeIterator begin(order_t order) { return BinaryTreeIterator(this, order); }
BinaryTreeIterator end() { return BinaryTreeIterator(); }
};
int main () {
typedef BinaryTreeNode<std::string> Node;
std::ostream_iterator<std::string> out( std::cout, " " );
Node a("a");
Node c("c");
Node b("b", &a, &c);
Node e("e");
Node h("h");
Node i("i", &h, 0);
Node g("g", 0, &i);
Node f("f", &e, &g);
Node d("d", &b, &f);
std::copy(d.begin(INORDER), d.end(), out);
std::cout << std::endl;
std::copy(d.begin(PREORDER), d.end(), out);
std::cout << std::endl;
std::copy(d.begin(POSTORDER), d.end(), out);
std::cout << std::endl;
return 0;
}