我有一个非常简单的二叉树结构,如:
struct nmbintree_s {
unsigned int size;
int (*cmp)(const void *e1, const void *e2);
void (*destructor)(void *data);
nmbintree_node *root;
};
struct nmbintree_node_s {
void *data;
struct nmbintree_node_s *right;
struct nmbintree_node_s *left;
};
有时我需要从另一个树中提取“树”,我需要获取“提取树”的大小,以便更新初始“树”的大小。
我在考虑两种方法:
1)使用递归函数,例如:
unsigned int nmbintree_size(struct nmbintree_node* node) {
if (node==NULL) {
return(0);
}
return( nmbintree_size(node->left) + nmbintree_size(node->right) + 1 );
}
2)以迭代方式(使用堆栈/队列)+对节点进行计数的前序/顺序/后序遍历。
您认为哪种方法更具“内存故障证明”/性能?
还有其他建议/提示吗?
注意:我可能会在将来对我的小项目使用此实现。所以我不想意外失败:)。
答案 0 :(得分:5)
只需使用递归函数。以这种方式实现起来很简单,并且不需要使其更加复杂化。
如果您“手动”执行此操作,您基本上最终会实现相同的操作,只是您不会将系统调用堆栈用于临时变量,而是使用您自己的堆栈。通常,这将没有任何优势超过更复杂的代码。
如果你后来发现程序中花费了大量时间来计算树的大小(这可能不会发生),你仍然可以开始分析事物并尝试手动实现的执行方式。但是,最好还是进行算法改进,比如在提取过程中已经跟踪了大小的变化。
答案 1 :(得分:2)
如果你的“非常简单”的二叉树不平衡,那么递归选项是可怕的,因为无约束的递归深度。迭代遍历具有相同的时间问题,但至少堆栈/队列在您的控制之下,因此您不需要崩溃。实际上,通过每个节点中的标志和一个额外的指针以及独占访问,您可以在没有任何堆栈/队列的情况下迭代树。
另一个选项是每个节点存储它下面的子树的大小。这意味着无论何时添加或删除某些内容,您都必须一直跟踪更新所有大小的根。因此,如果树不平衡,这将是一个沉重的操作。
如果树是平衡的,那么它不是很深。所有选项都是防故障的,并且性能是通过测量来估计的:-)但是基于你的树节点结构,要么它不平衡,要么就是你在玩指针最低位的标记玩傻游戏......
这可能没有多大意义。对于二叉树的许多实际用途(特别是如果它是二进制搜索树),您很快就会意识到它希望它能够平衡。因此,当你达到这一点时,请节省你的精力: - )
答案 2 :(得分:1)
这棵树有多大,你需要多久知道它的大小?正如......所说,递归函数是最简单的,可能是最快的。
如果树类似于10 ^ 3个节点,并且每秒更改10 ^ 3次,那么您可以保留一个外部计数,当您删除节点时会减少该计数,并在添加节点时递增。除此之外,简单是最好的。
就个人而言,我不喜欢任何需要使用额外信息(如计数和“向上”指针)来装饰节点的解决方案(尽管有时候我会这样做)。任何类似的额外数据都会使结构非规范化,因此更改它会涉及额外的代码和额外的错误机会。