Java 从子树节点聚合值的算法
我有树状结构中的对象,我想从子节点聚合状态信息,并用聚合状态更新父节点。假设节点A有子节点B1、B2,B1有子节点C1、C2、C3。每个节点都有一个状态属性 现在如果C1,C2,C3都完成了,那么我想把B1标记为完成。如果C4、C5、C6、C7是完整的,则B2是完整的。当B1和B2都完成时,将A标记为完成 我可以用蛮力方法检查这些节点并进行更新,有人能提出一个有效的算法来做吗 A{ 地下一层 {C1,C2,C3}, 地下二层 {C4,C5,C6,C7}Java 从子树节点聚合值的算法,java,algorithm,Java,Algorithm,我有树状结构中的对象,我想从子节点聚合状态信息,并用聚合状态更新父节点。假设节点A有子节点B1、B2,B1有子节点C1、C2、C3。每个节点都有一个状态属性 现在如果C1,C2,C3都完成了,那么我想把B1标记为完成。如果C4、C5、C6、C7是完整的,则B2是完整的。当B1和B2都完成时,将A标记为完成 我可以用蛮力方法检查这些节点并进行更新,有人能提出一个有效的算法来做吗 A{ 地下一层 {C1,C2,C3}, 地下二层 {C4,C5,C6,C7} }您需要后序遍历-首先访问节点的子节点,然
}您需要后序遍历-首先访问节点的子节点,然后递归地标记节点本身 类似于(伪代码):
我看不出你能逃避“暴力” 我会使用访问者设计模式
Eli Bendersky说得对,这个问题的一般答案是后序遍历 但是,为了提高效率,您必须使用您所知道的有关问题的所有信息。例如,如果您可以允许一些“过时”,那么最好在每个节点中缓存
complete
标志和时间戳
另一种可能性是节点的内部complete
状态很少改变。在这种情况下,向上传播完整性信息可能会更好。诸如此类:
class NodeWithCompletenessInfo : public Node {
private bool internalComplete; // Am I personally done?
private bool childrenComplete; // Are my children done?
public bool isComplete() {
return internalComplete && childrenComplete;
}
public void markComplete() {
internalComplete = true;
if( isComplete() )
parent.markChildComplete();
}
public void markIncomplete() {
if( isComplete() )
parent.markChildIncomplete();
internalComplete = false;
}
private void markChildComplete() {
for( child in children ) {
if( !child.isComplete() )
return;
childrenComplete = true;
}
if( isComplete() )
parent.markChildComplete()
}
private void markChildIncomplete() {
if( isComplete() )
parent.markChildIncomplete();
this.childrenComplete = false;
}
}
如果您知道哪些是叶节点,那么
A {
B1
{C1, C2 = false, C3},
B2
{C4, C5=false, C6=false, C7}
} // those not marked false are true ;)
not_complete_leaf_nodes_with_different_parents = [ C2 , C5]
mark_not_complete(node):
node.complete = false
if parent != null
mark_not_complete(node.parent)
for each in not_complete_leaf_nodes_with_different_parents:
mark_not_complete(each.parent)
任何有助于节省CPU周期/内存的东西。你的树有多大?你多久做一次手术?你能容忍一些“陈腐”吗?更有效地解决这一问题的方法通常不是通过低级优化,而是利用所有关于问题的知识。你可以在一些子树中缓存结果,当一个子树变得不完整时,在树上传播“不完整”标记,等等。我有大约5000棵树。每棵树有3到4层深。每个级别可以有大约20-50个节点。我计划每天运行一次此过程。可以根据需要运行单个树的进程。看起来,对于这些数据,我可能不需要缓存,但我将探索它。感谢您提供了这些重要参数。根据更新的频率,您可以计算自上次运行以来新的/更新的节点(如果与整个数据集相比较小),并向上传播更改。否则,从头开始自上而下重新计算。是的..每个节点上最后更新的时间戳是我算法的一部分,考虑一个“设计模式”来解决一个简单的算法树遍历。天哪。。。我们的日子到了;-)由于他没有具体说明他的代码,我想我应该给他一些更“强大”的工具(-1)我看不出访问者模式如何解决树遍历问题。是客户端决定了访问(遍历),而不是访问者。访客将对遍历作出响应。鉴于访问者不会维护遍历的状态,这正是访问者模式不起作用的原因。(如果说你想做一个“StatusCompleteVisitor”之类的)。除非我遗漏了一些东西,否则我认为访问者模式将不适合这个用例。如果您不断地向上提升树的状态,那么您可以避免遍历整个树,从而用对子节点的每次更改适当地标记父节点。当然,这要求孩子知道父母,但允许你通过简单的根检查来了解整体状态。@Yuval:是的,这是一种折衷。在某些情况下,它可能更有效,这取决于您更改孩子的频率和查询状态的频率。感谢Philippe提供的全面逻辑。
A {
B1
{C1, C2 = false, C3},
B2
{C4, C5=false, C6=false, C7}
} // those not marked false are true ;)
not_complete_leaf_nodes_with_different_parents = [ C2 , C5]
mark_not_complete(node):
node.complete = false
if parent != null
mark_not_complete(node.parent)
for each in not_complete_leaf_nodes_with_different_parents:
mark_not_complete(each.parent)