Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/442.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在Node.js中构建多分支树时出现性能问题_Javascript_Node.js_Memory_Memory Management_Tree - Fatal编程技术网

Javascript 在Node.js中构建多分支树时出现性能问题

Javascript 在Node.js中构建多分支树时出现性能问题,javascript,node.js,memory,memory-management,tree,Javascript,Node.js,Memory,Memory Management,Tree,我正在根据父节点ID将对象的平面层次结构转换为嵌套对象 问题是,当我输入更复杂的结构(更多和更深的子结构)时,这个过程需要很长时间才能完成 可能与内存或其他递归或冗余的低效使用有关?我不确定 守则: const people=[ { id:'738a8f8a', 父节点:空 }, { id:'d18fd69c', 父节点:“738a8f8a” }, { id:'b507c11d', 父节点:“738a8f8a” }, { id:'171d4709', 父节点:“b507c11d” }, { i

我正在根据父节点ID将对象的平面层次结构转换为嵌套对象

问题是,当我输入更复杂的结构(更多和更深的子结构)时,这个过程需要很长时间才能完成

可能与内存或其他递归或冗余的低效使用有关?我不确定

守则:

const people=[
{
id:'738a8f8a',
父节点:空
},
{
id:'d18fd69c',
父节点:“738a8f8a”
},
{
id:'b507c11d',
父节点:“738a8f8a”
},
{
id:'171d4709',
父节点:“b507c11d”
},
{
id:'471b1cee',
父节点:“b507c11d”
}
];
函数getBase(base){
用于(基地施工人员){
if(person['parentNode']==null){
返回人;
}
}
返回null;
}
函数getChildren(父级){
const values=people.filter((person)=>{
返回人['parentNode']==parent['id'];
});
返回Object.values(值);
}
函数buildHierarchy(base=null){
如果(基===null){
base=getBase(人);
如果(基===null){
返回null;
}
}
const children=getChildren(base).map((child)=>{
返回buildHierarchy(子级);
});
基['childrenNodes']=子节点;
返回基地;
}

log(buildHierarchy())我认为这里的主要瓶颈是算法
getChildren
迭代整个
people
数组,并为每个节点调用它。随着
人员
中元素数量的增加,成本也随之增加。自从我上次做算法分析已经有很长一段时间了,但是我有根据地猜测当前的实现是O(n^2)

例如,我可以使用
Map
来解决这个问题。我将遍历整个数组一到两次,以构建节点->子节点[]的
映射,这样我们就可以在递归时快速执行O(1)查找。这将有助于降低时间复杂度,但作为权衡,需要更多的内存,因为所有元素都在内存中多次存储

下面是一个例子:

function buildNodeChildLookup(people) {
  const nodeIdToNode = people.reduce((map, curr) => {
    map.set(curr.id, curr);
    return map;
  }, new Map());

  return people.reduce((map, curr) => {
    const children = map.get(curr.parentNode) || [];
    const childNode = nodeIdToNode.get(curr.id);
    children.push(childNode);
    map.set(curr.parentNode, children);
    return map;
  }, new Map());
}

// assume only one root!
const nodeIdToChildren = buildNodeChildLookup(people);

console.log(nodeIdToChildren)
// Map {
//   null => [ { id: '738a8f8a', parentNode: null } ],
//   '738a8f8a' => [
//     { id: 'd18fd69c', parentNode: '738a8f8a' },
//     { id: 'b507c11d', parentNode: '738a8f8a' }
//   ],
//   'b507c11d' => [
//     { id: '171d4709', parentNode: 'b507c11d' },
//     { id: '471b1cee', parentNode: 'b507c11d' }
//   ]
// }
现在我们有了“节点id”->“子节点”的快速查找,我们可以从根节点(其父节点为
null
)递归:

如您所见,
buildHierarchy
函数现在更轻了,因为它只是快速查找节点的子节点

总而言之:

const people=[
{
id:“738a8f8a”,
parentNode:null,
},
{
id:“d18fd69c”,
父节点:“738a8f8a”,
},
{
id:“b507c11d”,
父节点:“738a8f8a”,
},
{
id:“171d4709”,
父节点:“b507c11d”,
},
{
id:“471b1cee”,
父节点:“b507c11d”,
},
];
功能构建nodeChildlookup(人){
const nodeIdToNode=people.reduce((地图,当前)=>{
地图集(当前id,当前);
返回图;
},新地图());
返回人员。减少((地图,当前)=>{
const children=map.get(curr.parentNode)| |[];
const childNode=nodeIdToNode.get(curr.id);
push(childNode);
map.set(curr.parentNode,子节点);
返回图;
},新地图());
}
函数构建层次结构(节点、节点子节点){
const children=nodeToChildren.get(node.id)| |[];
返回{
…节点,
childNodes:children.map((child)=>buildHierarchy(child,nodeToChildren)),
};
}
//假设只有一个根!
const nodeIdToChildren=buildNodeChildLookup(人);
const root=nodeIdToChildren.get(null)[0];
log(buildHierarchy(root,nodeIdToChildren))
在写这个答案时,我看到了@cbr的,并认为这是相同的逻辑。但并非完全如此,而且似乎存在一个合理的性能差异(至少在Chrome中是这样),所以我仍然会发布这个

我无法用需要很长时间处理的真实数据来测试这一点,但我认为您的瓶颈是在
getChildren
函数中使用
filter
。对于每一个人,您都要浏览整个
人员
数组

我认为,在构建层次结构之前,只需预处理一次数据,就可以缩短时间。为此,我们可以创建一个数组,其中每个键都是一个人的ID,值是其子项的数组

这可以这样实现:

// For each person
const childMap = people.reduce((map, person) => {
  // If its parentNode is not already in the map
  if (!map.has(person.parentNode)) {
    // Add it
    map.set(person.parentNode, []);
  }
  // Then, push the current person into that parent ID's children Array
  map.get(person.parentNode).push(person);
  return map;
}, new Map());
function getChildren(parent) {
  return childMap.get(parent.id) || [];
}
然后,您的
getChildren
函数将如下所示:

// For each person
const childMap = people.reduce((map, person) => {
  // If its parentNode is not already in the map
  if (!map.has(person.parentNode)) {
    // Add it
    map.set(person.parentNode, []);
  }
  // Then, push the current person into that parent ID's children Array
  map.get(person.parentNode).push(person);
  return map;
}, new Map());
function getChildren(parent) {
  return childMap.get(parent.id) || [];
}
下面是一个完整的示例,连续运行100.000次:

const people=[
{
id:'738a8f8a',
父节点:空
},
{
id:'d18fd69c',
父节点:“738a8f8a”
},
{
id:'b507c11d',
父节点:“738a8f8a”
},
{
id:'171d4709',
父节点:“b507c11d”
},
{
id:'471b1cee',
父节点:“b507c11d”
}
];
const childMap=people.reduce((map,person)=>{
如果(!map.has(person.parentNode)){
map.set(person.parentNode,[]);
}
map.get(person.parentNode)、push(person);
返回图;
},新地图());
函数getBase(base){
用于(基地施工人员){
if(person.parentNode==null){
返回人;
}
}
返回null;
}
函数getChildren(父级){
返回childMap.get(parent.id)| |[];
}
函数buildHierarchy(base=null){
如果(基===null){
base=getBase(人);
如果(基===null){
返回null;
}
}
const children=getChildren(基本);
base.childrenNodes=children.map(buildHierarchy);
返回基地;
}
控制台时间('x');
对于(设i=0;i<100000;i++)buildHierarchy();

控制台。时间结束('x')您是否能够共享需要较长时间处理的样本数据?另外,你能更具体地说一下“很长一段时间”吗?我认为瓶颈是在
getChildren
函数中使用
filter
,但是使用真实数据进行测试非常有用!性能差异是由于我的实现复制了对象(
{…node}
)。如果实现更改为mutate
node.childNod