【问题标题】:Calculating paths through a tree for a match algorithm为匹配算法计​​算通过树的路径
【发布时间】:2015-11-04 23:34:58
【问题描述】:

我正在尝试开发一个函数来搜索树状结构并返回所有通过简单字符串测试 (indexOf) 的节点的完整路径。 我认为我有点接近,因为我可以处理任意深度的 N 个根节点,只要路径是线性的。当我分支时会出现问题,因为应该发生的是每个分支都变成一个包含其整个路径的不同数组。然而,正在发生的事情是每个分支都被合并到一个公共数组中,该数组表示发生分支的父级的所有子级下的匹配路径。

http://plnkr.co/edit/tbH0FW6T11F3iwNIBHdN?p=preview

显示正确结果的简单搜索,数据中没有分支

搜索显示错误/合并的结果与数据中的分支

在上图中,结果应包含三个元素,每个元素代表遍历的不同路径(字符串测试匹配“Z”)。 [1aa,1a,1],[1aba,1aa,1a,1],[2aa,2a,2]

注释掉数据中的“Zoe”条目以在线性和分支测试之间切换。

(function(){
  
  var data = 
  [
    {"name" : "Mike" , "id" : "1","children" : 
    [
      {"name" : "Jim" , "id" : "1a", "children" : 
      [
        {"name" : "Zoe" , "id" : "1aa", "children":[]}, //Uncomment for fail test
        {"name" : "Carrie" , "id" : "1ab", "children" :
        [
          {"name" : "Zane" , "id" : "1aba", "children":[]}
        ]}
      ]}
    ]},
    {"name" : "Allen" , "id" : "2","children" : 
    [
      {"name" : "Fred" , "id" : "2a", "children" : 
      [
        {"name" : "Zach" , "id" : "2aa", "children" :
        [
          {"name" : "Dean" , "id" : "2aaa", "children":[]}
        ]}
      ]}
    ]}
  ];
  
  var rFX = function(val,item){
    
    var subMatches =[];

    subMatches = (item.children.map(function(child){
      return rFX(val,child);
    }));
    
    subMatches = [].concat.apply([], subMatches);
    
    if(item.name.indexOf(val) > -1 || subMatches.length >0){
      subMatches.push(item.id);
    }
    
    return subMatches;
    
  };
  
  var result = data.map(function(item){
    return rFX("Z",item);
  });
  
  console.log(result);
 
})();

【问题讨论】:

  • 重新思考rFX 的返回值应该是什么样子。然后检查您对递归调用的结果所做的操作是否正确。
  • 现在重新思考,尝试不同的方法,因为我看不出这种方法如何避免合并子树。
  • 如何定义“子树”?它是一个路径数组的数组吗?
  • @Bergi,是的...最终结果中的每个数组都应该代表单个匹配节点的完整路径。目前,该算法将子路径/子树合并到一个数组中,而不是保持它们不同。 sn-p 中数据的算法的正确结果是 [1aa,1a,1],[1aba,1aa,1a,1],[2aa,2a,2]
  • 是的,您需要将多个路径数组合并为一个路径数组(其中每个路径仍然是一个数组)。但是你仍然有一系列路径。那么你现在打算用你当前的item.id 做什么呢? (发布的代码有什么作用?)

标签: javascript recursion


【解决方案1】:

我放弃了最初的方法,因为递归调用返回堆栈时合并数据的复杂性似乎太难了。

相反,我将问题分解为两个函数。

  1. getAllPaths :此函数遍历整个结构,计算到叶节点的所有可能路径的数组。这里不进行过滤。每个递归调用都会传递一个先前在其路径中处理过的节点数组。当函数到达一个没有更多子节点的节点时,它会将最终路径添加到数组中,然后将路径数组放在所有递归调用都已关闭的主数组上。

  2. filterPaths :此函数会蚕食从 getAllPaths 返回的集合中的每个路径(自下而上),直到找到匹配项,从而为我们留下匹配项的路径集合。

http://plnkr.co/edit/tbH0FW6T11F3iwNIBHdN

(function() {

  var data = [{
    "name": "Mike",
    "id": "1",
    "children": [{
      "name": "Jim",
      "id": "1a",
      "children": [{
          "name": "Zoe",
          "id": "1aa",
          "children": []
        },
        {
          "name": "Carrie",
          "id": "1ab",
          "children": [{
            "name": "Zane",
            "id": "1aba",
            "children": []
          }]
        }
      ]
    }]
  }, {
    "name": "Allen",
    "id": "2",
    "children": [{
      "name": "Fred",
      "id": "2a",
      "children": [{
        "name": "Zach",
        "id": "2aa",
        "children": [{
          "name": "Dean",
          "id": "2aaa",
          "children": []
        }]
      }]
    }]
  }];

  //Return all complete paths in the data (paths to leaf nodes)
  var getAllPaths = function(items) {

    //Array which all calls will push their completed path arrays onto.
    var master = [];

    //Recursive call to travese every path in our data.
    var rFXi = function(item, itemArray) {

      itemArray.push(item); //always push current item

      //If we still have children process them recursively
      if (item.children && item.children.length > 0) {
        item.children.forEach(function(child) {
          rFXi(child, itemArray.slice());
        });
      }

      //else we are at a leaf node so push complete path array onto master.
      else {
        master.push(itemArray);
      }

    };

    //boot up our recursive calls
    items.forEach(function(child) {
      rFXi(child, []);
    });

    return master;
  };

  //Test our paths against a comparator function and,
  //remove all path segments beneath a terminal match
  //and then return only those paths remaining with a
  //length greater than zero.
  var filterPaths = function(paths, comparator) {

    paths.forEach(function(path) {

      var matched = false,
        length = path.length,
        i;

      for (i = path.length - 1; i >= 0 && !matched; i--) {
        if (comparator(path[i], "D")) {
          matched = true;
        } else {
          //remove non matching leaf
          path.splice(i, 1);
        }
      }

    }); //forEach end

    return paths.filter(function(path) {
      return path.length > 0;
    });

  };

  //Retrieve all paths to leaf nodes in our "tree"
  var paths = getAllPaths(data);
  console.log(paths);

  //Now filter those paths for matching segments
  paths = filterPaths(paths, function(item, val) {
    return item.name.indexOf(val) > -1;
  });
  console.log(paths);


})();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-03
    • 2014-10-20
    • 1970-01-01
    • 2011-09-27
    • 2017-08-19
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    相关资源
    最近更新 更多