template<typename T>
void traverse_binary_tree(BinaryTreeNode<T>* root,int order = 0)// 0:pre, 1:in , 2:post
{
if( root == NULL ) return;
if(order == 0) cout << root->data << " ";
traverse_binary_tree(root->left,order);
if(order == 1) cout << root->data << " ";
traverse_binary_tree(root->right,order);
if(order == 2) cout << root->data << " ";
}
有没有更好的方法来编写这个函数?
答案 0 :(得分:8)
没有
开玩笑。我认为它看起来非常有效。
为了便于阅读,我会枚举订单值。
...
enum TOrder {ORDER_PRE, ORDER_IN, ORDER_POST};
template<typename T>
void traverse_binary_tree(BinaryTreeNode<T>* root,TOrder order = ORDER_PRE) {
...
答案 1 :(得分:4)
但是,通过分离出两个问题,您可以获得更大的灵活性:遍历树,并对数据执行某些操作。您可能希望以各种方式分析和操作数据以及打印数据,最好不要为每个操作复制遍历逻辑。相反,您可以添加额外的模板参数,以允许按顺序执行前,后或按顺序操作的任意组合:
// PRE, IN and POST operations are unary function objects which can
// take Node<T>* as their argument
template <typename T, typename PRE, typename IN, typename POST>
void traverse(Node<T>* root, PRE& pre, IN& in, POST& post)
{
if (!root) return;
pre(root);
traverse(root->left, pre, in, post);
in(root);
traverse(root->right, pre, in, post);
post(root);
}
// This can be used as a template argument to denote "do nothing".
struct Nothing { void operator()(void*) {} } nothing;
// Usage example - print the nodes, pre-ordered
void print(Node<int>* node) {std::cout << node->data << " ";}
traverse(root, print, nothing, nothing);
// Usage example - find the sum of all the nodes
struct Accumulator
{
Accumulator() : sum(0) {}
void operator()(Node<int>* node) {sum += node->data;}
int sum;
};
Accumulator a;
traverse(root, a, nothing, nothing);
std::cout << a.sum << std::endl;
答案 2 :(得分:3)
这取决于你想要用它做什么 - 以及可能重构它来模板化顺序,或者分离三种遍历类型,你可能想把它变成一个内部迭代,允许任何东西访问数据在树上:
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;
}
}
(您可能还需要const F&amp;和F&amp;版本,而不是简单的复制,值传递函数参数,它可以让您传递可变的函子并产生结果;按值可以正常只要您的仿函数没有成员变量或构造函数)
或者您可以创建表示三次遍历的迭代器,允许您使用std :: copy写入输出;然而,这将是更多的代码,并不会仅仅为此目的。但是,创建迭代器将允许处理大型树而不会发生堆栈溢出,因为您必须在每个节点中具有“父”指针,或者让迭代器维护显式的访问节点堆栈。
虽然功能本身变得非常简单:
std::ostream_iterator<std::string>(std::cout, " ");
std::copy(d.begin(order), d.end(), out);
使用迭代器不会简化实现,无论是在loc方面还是实际上都能够跟踪正在发生的事情:
#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;
}
答案 3 :(得分:2)
我们可以“重新排序循环”
enum {post = 0x0101, in = 0x1001, pre = 0x1010};
template<typename T>
void traverse_binary_tree(BinaryTreeNode<T>* root,int order = pre)
{
if( root == NULL ) return;
if(order & 0x0001) traverse_binary_tree(root->left,order);
if(order & 0x0100) traverse_binary_tree(root->right,order);
cout << root->data << " ";
if(order & 0x0010) traverse_binary_tree(root->left,order);
if(order & 0x1000) traverse_binary_tree(root->right,order);
}
但它更多的是让它比简单更有趣。 :-)但是,代码在这里重复两次,而不是三次。
为了避免“幻数”你可以这样写:
enum {
left_before = 1 << 0,
left_after = 1 << 1,
right_before = 1 << 2,
right_after = 1 << 3,
};
int const pre = left_after | right_after ;
int const in = left_before | right_after ;
int const post = left_before | right_before;
/* The function body is fixed in the same way */
答案 4 :(得分:1)
您可以将order
移动到模板参数。
template<typename T, int order> // 0:pre, 1:in , 2:post
void traverse_binary_tree(BinaryTreeNode<T>* root)
{
if( root == NULL ) return;
if(order == 0) cout << root->data << " ";
traverse_binary_tree<T,order> (root->left);
if(order == 1) cout << root->data << " ";
traverse_binary_tree<T,order> (root->right);
if(order == 2) cout << root->data << " ";
}
每个if(order ==
中的两个将在函数的每个实例化中编译出来。
答案 5 :(得分:1)
我可能会这样: enum TraversalOrder {PreOrder,InOrder,PostOrder};
template<typename T>
void traverse_binary_tree_preorder(BinaryTreeNode<T>* root)
{
if( !root ) return;
cout << root->data << " ";
traverse_binary_tree_preorder(root->left,order);
traverse_binary_tree_preorder(root->right,order);
}
template<typename T>
void traverse_binary_tree_inorder(BinaryTreeNode<T>* root)
{
if( !root ) return;
traverse_binary_tree_inorder(root->left,order);
cout << root->data << " ";
traverse_binary_tree_inorder(root->right,order);
}
template<typename T>
void traverse_binary_tree_postorder(BinaryTreeNode<T>* root)
{
if( !root ) return;
traverse_binary_tree_postorder(root->left,order);
traverse_binary_tree_postorder(root->right,order);
cout << root->data << " ";
}
template<typename T>
void traverse_binary_tree(BinaryTreeNode<T>* root,TraversalOrder order = InOrder)
{
switch(order)
{
case PreOrder:
return traverse_binary_tree_preorder(root);
case PostOrder:
return traverse_binary_tree_postorder(root);
default:
return traverse_binary_tree_inorder(root);
}
}
每个函数都尽可能简单,如果你在编译时知道你需要哪个函数,你可以调用你想要的直接遍历函数。
答案 6 :(得分:0)
@Vi,您将进入功能专长的角落,请参阅https://stackoverflow.com/search?q=function+partial+specialization
测试顺序效率不高且不优雅,您应将traverse_binary_tree委托给只执行一项作业的模板类。 (通过专门化)这个模板类还应该为成员BinaryTreeNode *启动递归算法。
答案 7 :(得分:0)
有没有更好的方法来写这个 功能
如果它不是一个平衡的二叉树,它可能容易出现堆栈溢出。迭代地编写它将避免这种情况。然而,这可能不是你所追求的,因为我怀疑这是递归的学术练习。如果这是一个真实世界的项目,当有效的集合和关联容器已经存在时,您可能会被问到为什么要实现二叉树。
你可以用这种方式重写函数,以便与单入口,单出口(试图坚持你的风格)相吻合:
template<typename T>
void traverse_binary_tree(BinaryTreeNode<T>* root,int order = 0)// 0:pre, 1:in , 2:post
{
if( root != NULL )
{
if(order == 0) cout << root->data << " ";
traverse_binary_tree(root->left,order);
if(order == 1) cout << root->data << " ";
traverse_binary_tree(root->right,order);
if(order == 2) cout << root->data << " ";
}
}
有些人可能会发现这样做会更好,但其他人则不会(由于异常处理,SESE在大多数项目中都不能实际执行)。
如果你真的想要超越(仍然用于学术目的),你可以实现预迭代,有序和后顺序遍历的树迭代器。这将在没有递归的情况下遍历树,并允许您将树遍历细节与打印出节点分离。这肯定不是一项微不足道的任务,特别是在C ++中没有与语言级相当的生成器/协同程序。
您还可以避免使用幻数(0,1,2)进行预订,按顺序和后顺序遍历,而是使用命名常量。
答案 8 :(得分:0)
我将它写成三个独立的功能。这不是在编写代码方面的简化,而是在阅读和理解方面的简化。您每次都没有查看文档以记住哪个int
是哪种遍历。 (当然,这可以通过使用枚举来解决。)
在不使用任何模板魔法的情况下分离if开关也有可忽略的速度优势。保持简单。