Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/378.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:涉及I/O的定向树的DFS遍历_Javascript_Node.js_Tree - Fatal编程技术网

Javascript:涉及I/O的定向树的DFS遍历

Javascript:涉及I/O的定向树的DFS遍历,javascript,node.js,tree,Javascript,Node.js,Tree,给定一个每个节点具有可变子节点数的有向树T,我想找到一条路径,路径的大小为“good”节点的大小,从根开始 每个节点都有一个isGood()方法和一个getChildren()方法,可以按预期工作 一个简单的DFS递归解决方案如下:(如果我错了,请纠正我) 这个解决方案行不通:一个节点的子节点的所有getChildren方法都将被一次调用,因此它实际上将执行BFS。更糟糕的是,return语句与匿名回调函数关联,并将在封闭函数完成运行后执行 很明显,需要某种流量控制机制。这个问题的简单而优雅的解

给定一个每个节点具有可变子节点数的有向树T,我想找到一条路径,路径的大小为“good”节点的大小,从根开始

每个节点都有一个
isGood()
方法和一个
getChildren()
方法,可以按预期工作

一个简单的DFS递归解决方案如下:(如果我错了,请纠正我)

这个解决方案行不通:一个节点的子节点的所有getChildren方法都将被一次调用,因此它实际上将执行BFS。更糟糕的是,return语句与匿名回调函数关联,并将在封闭函数完成运行后执行

很明显,需要某种流量控制机制。这个问题的简单而优雅的解决方案是什么


更新:我接受了塞巴斯蒂安的答案,因为它通过递归解决了这个问题,这就是我提出问题的方式。我还发布了一个使用库循环的答案,这就是我最终使用的。Sebastien很好地对这两种方法进行了基准测试。(扰流板:性能是相同的)

我现在不是100%关注,但我几乎可以肯定这是适合您的正确解决方案(如果不是seriestasks,我愿意打赌Async.js中有另一个控制流可以实现这一点。

首先,我认为您必须调用findGoodPath(children[i],depth+1)如果希望深度等于路径大小

然后,您确实存在闭包问题。在异步调用中,您总是使用一个您不想要的节点实例进行闭包

一种方法是:

node.getChildren((function(_node) {
  return function(children){
    for (var i=0; i<children.length; i++){
      var result = findGoodPath(children[i], depth);
        if (result){
          return result.concat([_node]);
        }
      }
    });
})(node));
是作为同步调用编写的,而findGoodPath是一个异步函数,因此它也必须使用回调编写

希望能有帮助

ps:有一个JSFIDLE会有帮助

更新:只是一个尝试。由于我无法测试,它不起作用,但这是我的想法。我无法确定是否需要在第二个findGoodPath调用中创建另一个作用域,就像在getChildren调用中一样

function findGoodPath(node, depth, callback){
  if(!node.isGood()){
    return callback(null);
  } else if (depth==PATH_SIZE){
    return callback([node]);
  }
  node.getChildren((function(_node, _callback) {

    return function(children){
      var node = _node, callback = _callback;

      for (var i=0; i<children.length; i++){
        findGoodPath(children[i], depth, function(result) {
          if (result){
            return callback(result.concat([node]));
          }
        });
      }
    });
  })(node, callback));
}
函数findGoodPath(节点、深度、回调){
如果(!node.isGood()){
返回回调(null);
}else if(深度==路径大小){
返回回调([node]);
}
getChildren((函数(\u节点,\u回调){
返回函数(子级){
var node=\u node,callback=\u callback;

对于(var i=0;iOK),有几种方法可以实现异步DFS遍历。由于异步递归有变得有点丑陋的趋势,我决定放弃递归

我首先使用while循环而不是递归重新实现了函数的同步版本:

function findGoodPathLoop(root){
    var nodesToVisit = [{data: root, path:[]}];
    while (nodesToVisit.length>0){
        var currentNode = nodesToVisit.pop();
        if (currentNode.data.isGood()){
            var newPath = currentNode.path.concat(currentNode.data);
            if (newPath.length==PATH_SIZE){
                return newPath;
            } else {
                var childrenNodes = currentNode.data.getChildren().map(function(child){
                    return {data: child, path: newPath};
                });
                nodesToVisit = nodesToVisit.concat(childrenNodes);
            }
        }
    }
    return null;
}
注意:我为每个节点保存了整个路径,这不是必须的,您可以只保存深度并维护当前路径的数组,尽管它有点混乱

然后,我使用库将此函数转换为异步函数,将标准的
while()
函数替换为异步的
while()


这不是一个漂亮的例子,但它可读性好,而且可以完成任务。

我的错,当然函数需要一个回调参数。非函数代码示例只是为了展示将其转换为异步的一些问题。我将在上午查看它,但我当前的方向是转向非递归解决方案,然后使用它使用js异步库中的Actions来控制流。将异步函数和递归结合起来是一个很好的挑战,但我担心它会创建不可维护的代码(至少对我来说是这样)。没错!但是能够以异步的方式完成这项工作将非常有效,而且肯定是一个很好的挑战:)我无法让它工作。你能给我一把小提琴吗?你可以用我的小提琴作为基础:给你:(我刚刚签入了jsperf,对于这种类型的节点,两种方法的速度都相同:)
var result = findGoodPath(children[i], depth)
function findGoodPath(node, depth, callback){
  if(!node.isGood()){
    return callback(null);
  } else if (depth==PATH_SIZE){
    return callback([node]);
  }
  node.getChildren((function(_node, _callback) {

    return function(children){
      var node = _node, callback = _callback;

      for (var i=0; i<children.length; i++){
        findGoodPath(children[i], depth, function(result) {
          if (result){
            return callback(result.concat([node]));
          }
        });
      }
    });
  })(node, callback));
}
function findGoodPathLoop(root){
    var nodesToVisit = [{data: root, path:[]}];
    while (nodesToVisit.length>0){
        var currentNode = nodesToVisit.pop();
        if (currentNode.data.isGood()){
            var newPath = currentNode.path.concat(currentNode.data);
            if (newPath.length==PATH_SIZE){
                return newPath;
            } else {
                var childrenNodes = currentNode.data.getChildren().map(function(child){
                    return {data: child, path: newPath};
                });
                nodesToVisit = nodesToVisit.concat(childrenNodes);
            }
        }
    }
    return null;
}
function findGoodPathAsync(root, pathCallback){
    var result = null;
    var nodesToVisit = [{data: root, path:[]}];
    async.whilst(
        function(){
            return nodesToVisit.length>0 && result==null ;
        },
        function(next) {
            var currentNode = nodesToVisit.pop();
            if (currentNode.data.isGood()){
                var newPath = currentNode.path.concat(currentNode);
                if(newPath.length==PATH_SIZE){
                    result = newPath;
                    next();
                } else {
                    currentNode.data.getChildren(function(children){
                        var childrenNodes = children.map(function(child){
                            return {data: child, path: newPath};
                        });
                        nodesToVisit = nodesToVisit.concat(childrenNodes);
                        next();
                    });
                }
            } else {
                next();
            }
        },
        function(err) {
            //error first style callback
            pathCallback(err, result);
        }
    );
}