Javascript 如何遍历一个节点有多个父节点的图并求和值?
我从JSON导出接收到以下数据:Javascript 如何遍历一个节点有多个父节点的图并求和值?,javascript,algorithm,recursion,tree,Javascript,Algorithm,Recursion,Tree,我从JSON导出接收到以下数据: nodes = [ {id:1,data:29,parentOf:[]}, {id:2,data:31,parentOf:[1,8,7]}, {id:3,data:41,parentOf:[2,1]}, {id:4,data:89,parentOf:[3,2,1]}, {id:5,data:71,parentOf:[4,3,2,1,9,2,8,7]}, {id:6,data:11,parentOf:[5,4,3,2
nodes = [
{id:1,data:29,parentOf:[]},
{id:2,data:31,parentOf:[1,8,7]},
{id:3,data:41,parentOf:[2,1]},
{id:4,data:89,parentOf:[3,2,1]},
{id:5,data:71,parentOf:[4,3,2,1,9,2,8,7]},
{id:6,data:11,parentOf:[5,4,3,2,1]},
{id:7,data:59,parentOf:[]},
{id:8,data:43,parentOf:[7]},
{id:9,data:97,parentOf:[2,8,7]}
]
这是一个图中的数据结构,其中一个节点可以有零个或多个父节点。图形已展平,以便导出到节点数组中。
现在,我需要聚合数据字段的值
如何遍历此图以获得每个节点的数据总和
编辑:所提供示例的最终结果如下:
[
{id:1,sum:29},
{id:2,sum:162}, // 31+102+29
{id:3,sum:203}, // 41+162
{id:4,sum:292}, // 89+203
{id:5,sum:622}, // 71+292+259
{id:6,sum:633}, // 11+622
{id:7,sum:59},
{id:8,sum:102}, // 43+59
{id:9,sum:259} // 97+162
]
编辑2:这是我根据上述数据结构绘制的图形:
我现在看到,在节点的parentOf数组中,有一些冗余信息
尽管提供的示例只有一个没有父节点的节点(id:6),但我正在寻找一个解决方案来处理没有父节点的节点不止一个的正常情况。所以我认为图遍历应该从叶节点开始,即id:1和id:7
非递归方法(如果可能的话)将不胜感激。我想我遗漏了一些东西。。。 请查看以下内容:
node.data
和所有parent.data
id
和sum
属性的对象var节点=[
{id:1,data:29,parentOf:[]},
{id:2,数据:31,父项:[1,8,7]},
{id:3,data:41,parentOf:[2,1]},
{id:4,data:89,父项:[3,2,1]},
{id:5,数据:71,父母:[4,3,2,1,9,2,8,7]},
{id:6,data:11,父项:[5,4,3,2,1]},
{id:7,data:59,parentOf:[]},
{id:8,data:43,parentOf:[7]},
{id:9,data:97,父项:[2,8,7]}
];
var结果=节点
.map((项目,i,项目)=>{
设r=Object.create(null);
r、 id=item.id;
r、 总和=项的父项减少((上一个,下一个,j)=>{
让parent=(items.filter(x=>x.id==next)[0]|{});
让parentData=parent.data | | 0;
返回prev+parentData;
},项目数据);
返回r;
})
;
console.log('result',result)
使这个问题变得困难的是,parentOf
列表有时不仅包含直接子代的id值,还包含(一些)更远程子代的id值。因此,真正的图形并不明显
例如,示例数据将节点9列为节点2、8和7的父节点。然而,这张图片显示,其中只有2人是9岁的直系子女,而8岁和7岁则是较远的后代。节点1也是9的后代,未列在节点9的parentOf
属性中
真正直接的孩子的信息可以从给出的数据中推断出来,但这需要一些工作。然而,需要这些信息才能正确计算总和
注意,由于这种冗余输入,不可能在图形中定义三角形。假设节点A将节点B和C作为子节点,节点B将节点C作为子节点:这样的三角形在上述数据结构中不可编码,因为A和C之间的链接将被视为冗余的“孙子”链接
在我这里介绍的解决方案中,我使用了记录子代和直接子代的方法,因为这样可以比数组更快地查找
我还创建了一个用于使节点可以通过其id访问的
当在图中检测到循环时,将引发异常
根据要求,该算法不依赖于递归
代码如下:
函数树(节点){
//获取节点数组的副本,以便原始节点不会发生变化
var nodes=nodes.map(node=>({
id:node.id,
childrenSet:new Set(node.parentOf),//转换为Set进行快速查找
childrenSet2:新集合(node.parentOf),//复制
子体集:新集(node.parentOf),//将包含所有子体
总和:node.data
}) );
//为了更快地查找,请创建一个包含由其node.id设置关键帧的节点的映射。
var hash=newmap(nodes.Map(node=>[node.id,node]);
//为每个节点创建完整的子体集
nodesCopy=nodes.slice();
while(true){
//获取没有(更多)子节点的第一个节点的索引
设i=nodesCopy.findIndex(node=>!node.childrenSet.size);
如果(i<0)break;//未找到:那么我们就完成了
let id=nodesCopy.splice(i,1)[0].id;//提取找到的节点id
nodesCopy.forEach(父项=>{
如果(parent.childrenSet.has(id)){//找到了它的父项
//用节点的子列表扩展父节点的子列表
parent.degenantset=新集合([…parent.degenantset,
…hash.get(id.genderantset]);
/不要再考虑这个节点。
//在某一点上,父代可能会变成一片叶子
parent.childrenSet.delete(id);
}
});
}
if(nodesCopy.length)抛出“异常:图形有循环”;
//创建真正的子集合(仅直接子集合,无“冗余”):
nodes.forEach(node=>{
node.childrenSet=新集合(node.childrenSet2);
[…节点.子节点集]
//收集后代
.reduce((远程,id)=>
新集合([…远程,…hash.get(id).degenantset]),新集合()
//从孩子们的集合中移除任何一个
//(因此不再有“大”孩子了)
.forEach(id=>node.childrenSet.delete(id));
});
//自下而上计算总和
nodesCopy=nodes.slice();
while(true){
设i=nodesCopy.findIndex(node=>!node.childrenSet.size);
如果(i<0)断裂;
设leaf=nodesCopy.splice(i,1)[0];
nodesCopy.forEach(父项=>{
if(parent.childrenSet.has(leaf.id)){
parent.sum+=leaf.sum;