说我有一个项目列表,例如5张桌子,5把椅子等。 每张桌子由4条腿和一个桌面组成。 每条腿由2块木头组成,桌面由5块木头和4个螺丝等组成。
我有一个项目组件列表,它存储在一个包含列(item1,item2,amount)的数据库中,其中item2是item1的一个组件,而' amount'是所需的item2数量。
我想计算最低级别材料列表的总净需求,而不考虑层次结构级别的数量,是否有最佳方法可以做到这一点?
如果重要,我打算用Java做这件事。
答案 0 :(得分:3)
您实际上已经描述过您的解决方案了!
你可以做的是创建一棵树。实际上,有几棵树,因为你提到的椅子和桌子都是独立的树木。
节点应该包含item1,其子节点应该是其每个组件,以及它所需的子节点数。所以,你有一个孩子的名单,即(Wood,2)(Nail,4)
在创建节点时,将它们添加到地图中,以便您可以轻松跟踪项目是否已制作,以便您可以使用相同的项目。应始终将没有父节点的节点添加到树列表中。应该从树的列表中删除具有父节点的节点(树根是此定义中的顶级组件)。
填充树时,请使用以上信息。没有父=新树。然后检查它是否使用任何树来构建其组件。如果是,请从树列表中删除该树。
所以我们假设我们有腿椅桌面椅子桌面
我们将Leg添加为root。它里面有儿童木材和钉子,我们创造了它。
然后我们添加使用Leg的Chair,所以我们从树列表中删除Leg并将其添加到Chair,并将Chair添加到树列表中。
TableTop由wood构成,因此我们将Wood节点添加为子节点,但TableTop和Chair不相关,因此TableTop现在是一棵新树。
ChairTop是Chair的一部分,所以我们将它作为一个孩子添加到Chair中,如果不是木头,它的部分被添加为节点。
Desk由TableTop组成,因此它成为根,而TableTop不再是树的根。
最终结果是桌椅作为树木; TableTop,ChairTop,Leg作为节点;木头和其他任何叶子节点。
所有树木的最低级别材料是基本组件。
完成后,您可以编写一个基本上遍历子项的方法,并传回一个项目/计数列表,以便正确地将所有内容相乘。
答案 1 :(得分:2)
我认为树不是最好的结构。请考虑以下图表:
这里的椅子由2根木棍和4块木板制成。您还可以创建支持,用1根原木制作4根木棍,称1根木棒需要0.25根原木。
但是,不是树。您可以通过创建2个raw wood
节点将其强制为树,但是在更大的图表上会导致巨大的内存效率低下。
你所拥有的是一个Oriented graph with no oriented cycles(定向循环会坏,就像你需要锤子制铁,但你需要铁来制造锤子)。
现在,如何有效地查找基本元素?我们假设在程序运行期间不会更改数据库,这将允许我们进行一些优化。如果您正在寻找时间效率,我建议每个节点都有一个创建此节点元素所需的Map<String, Double>
原始地图。要初始化此映射,只需将此节点子节点的所有映射相加,再乘以创建此元素所需元素的系数。如果孩子的地图尚未初始化,则递归初始化。
问题是如何经常查看数据库?为了更方便,我会在名称和现有节点之间保留一个额外的Map<String, Node>
地图,就像你看到那把椅子需要木棍一样,你可以看看你是否已经使用木棍做其他事情,因此你没有需要重新计算他们的原材料。所以你只在数据库中查看你需要的东西,而不是两次相同的东西。
或者对于较小的数据库,您可以在开始时初始化整个图形,但是如果没有请求某些项目,您就会冒着浪费时间和内存的风险。
修改强>
如果我们想让数据库改变,问题会变得复杂,让我们考虑更多的节点:
如果红线改变了它的值(例如,找到了创建预处理木材的更有效方法),那么我们需要使所有橙色节点无效(通过某些isValid
标志或其他东西)。如果每个节点都记得它的父母,我们可以很容易地做到这一点。
当请求chair
的材料时,需要重新验证它,因此它会询问wooden stick
,还需要重新验证,等等。但是,chair
会要求wooden plank
重新评估,但不会导致processable wood
重新评估,因为它仍然有效从wooden stick
被验证的时间开始。
@Compass:这就是你得到的行为,因为这个图是而不是一棵树。如果它是树,它看起来像这样:
(实际上是forest,我们必须首先省略方向,但想法是一样的)现在想象保持更新的东西是多么困难。
答案 2 :(得分:0)
尝试一下:
HashMap<String, Integer> bom;
void componentLookup(String itemCode) {
List<ComponentEntry> ces = findByItem1(itemCode);
for (ComponentEntry ce : ces) {
if (ce.item2 == null) {
// component without child-components, update the BOM...
if (bom.containsKey(ce.item1)) {
bom.put(ce.item1, bom.get(ce.item1) + ce.amount);
} else {
bom.put(ce.item1, ce.amount);
}
} else {
// component with child-components, proceed deeper lookup...
componentLookup(ce.item2);
}
}
}
答案 3 :(得分:0)
您要求的基本上是Tree Traversal,是的,有一些最佳算法可以访问树。 我相信这是一棵树,因为你不想拥有循环依赖,即你不希望item1依赖于item2,并且同时广告item2依赖于item1(通常你不想要更多复杂的周期)。
有许多算法可用于depth-first-search和breadth-first-search。您可以选择最适合您的时间和空间需求的(您可以在提供的链接中找到有关时间和空间的其他信息)。您还可以找到Java代码。