Javascript 在树结构中查找所有唯一路径

Javascript 在树结构中查找所有唯一路径,javascript,node.js,algorithm,Javascript,Node.js,Algorithm,假设我有一个文本文件中的目录结构 root1 child1 child2 grandchild1 grandchild2 child3 root2 child1 child2 grandchild1 greatgrandchild1 如何将上述树结构转换为如下所示的嵌套数组: [ [ "root1", "child1" ], [ "root1", "child2",

假设我有一个文本文件中的目录结构

root1
    child1
    child2
        grandchild1
        grandchild2
    child3
root2
    child1
    child2
        grandchild1
            greatgrandchild1
如何将上述树结构转换为如下所示的嵌套数组:

[
    [ "root1", "child1" ],
    [ "root1", "child2", "grandchild1" ],
    [ "root1", "child2", "grandchild2" ],
    [ "root1", "child3" ],
    [ "root2", "child1" ],
    [ "root2", "child2", "grandchild1", "greatgrandchild1" ]
]
编辑

var fs = require("fs");
var treeGrapher = require("./lib/treeGrapher.js");
var uniquePaths = require("./lib/uniquePaths.js");  

var tmpl = fs.readFileSync("./director.tmpl.txt", "utf8");
var graphs = treeGrapher(tmpl); //returns an array of trees
var paths = arrange(graphs);

/**    
[ 
    [ "root1", "rootchild1" ],
    [ "root1", "child2", "gc1" ],
    [ "root2" ],
    [ "root3", "root3-child1" ]
]
*/

function arrange(trees) {
    var bucket = [];
    trees.forEach(function(list) {
        uniquePaths(list).forEach(function(arr) {
            bucket.push(arr);
        });
    });
    return bucket;
}
越走越远,但递归遍历树时仍然存在问题

var $text = ''
+ 'root1\n'
+ '  r1 child1\n'
+ '  r1 child2\n'
+ '    r1 grandchild1\n'
+ '    r1 grandchild2\n'
+ '  r1 child3\n'
+ 'root2\n'
+ '  r2 child1\n'
+ '    r2 c1\n'
+ '      r2 c1 g1\n'
+ '  r2 child2\n'
+ '    r2 grandchild1\n'
+ '      r2 greatgrandchild1\n'
+ 'test!\n'
+ 'root3\n'
+ '  r3 child1\n'
+ '    r3 c1\n'
+ '      r3 c1 g1\n'
+ '  r3 child3\n'
+ '    r3 grandchild1\n'
+ '      r3 greatgrandchild1';

var dirGen = (function(trees) {
  "use strict";

  var indent = /[\s\t]/g;
  var lTrim = /[\s\t]*/;
  var $trees = decompose(trees);
  var $root = [];

  function init() {
    var paths = $trees.map(treeMap)
    $test(paths);
  }

  function treeMap(tree, n, arr) {
    var base = new LinkedList();
    return bfs(-1, tree, base);
  }

  function bfs(n, tree, base) {
    var l, t;
    n++;
    //base case
    if (n === tree.length) return trails(base);
    l = tree.length;
    t = tree[n];
    var cur = { label: t.replace(lTrim, ""), depth: depth(t), children: [] };
    //set root
    if (n === 0) {
      base.tree = cur;
      return bfs(n, tree, base);
    }

    base.push(cur);

    return bfs(n, tree, base);

  }

  function depth(str) {
    var d = str.match(indent);
    if (d === null) return 0;
    return d.length;
  }

  function trails(arr) {
    return arr;
  }

  function LinkedList() {}

  LinkedList.prototype.push = function(node) {   
    var l = this.tree.children.length;
    var j = l - 1;
    if (l === 0) {
      this.tree.children.push(node);
      return;
    }
    //walk through children array in reverse to get parent
    while (j > -1) {
      var d = this.tree.children[j].depth;
      //child
      if (node.depth > d) {
        console.log(this.tree.children[j], node)
        return this.tree.children[j].children.push(node);
      }
      //sibling
      if (node.depth === d) {

      }

      j--;
    }

  }

  function decompose(arr) {
    var treeBreak = /[\r\n](?=\w)/gm;
    var lines = /[\r\n]+(?!\s)*/g; 
    return arr.split(treeBreak).map(function(str) {
      return str.split(lines)
    });
  }

  function $test(str) {
    var json = JSON.stringify(str, null, 2);
    var wtf = "<pre>" + json + "</pre>";
    document.write(wtf);
  }

  return init;

})($text);

dirGen();
var$text=''
+“root1\n”
+'r1 child1\n'
+'r1 child2\n'
+'r1\n'
+“r1-2\n”
+'r1 child3\n'
+“root2\n”
+“r2儿童1\n”
+“r2 c1\n”
+“r2 c1 g1\n”
+“r2儿童2\n”
+“1\n”
+“r2曾孙1\n”
+“测试\不
+“root3\n”
+“r3儿童1\n”
+“r3 c1\n”
+“r3 c1 g1\n”
+“r3儿童3\n”
+“r3-1\n”
+“r3曾孙1”;
var dirGen=(函数(树){
“严格使用”;
var indent=/[\s\t]/g;
var lTrim=/[\s\t]*/;
var$trees=分解(树);
var$root=[];
函数init(){
变量路径=$trees.map(treeMap)
$test(路径);
}
函数树映射(树,n,arr){
var base=newlinkedlist();
返回bfs(-1,树,基);
}
函数bfs(n,树,基){
变量l,t;
n++;
//基本情况
如果(n==树长)返回路径(基);
l=树的长度;
t=树[n];
var cur={label:t.replace(lTrim,“”),depth:depth(t),children:[]};
//扎根
如果(n==0){
base.tree=cur;
返回bfs(n,树,基);
}
基推(cur);
返回bfs(n,树,基);
}
功能深度(str){
var d=str.match(缩进);
如果(d==null)返回0;
返回d.length;
}
功能跟踪(arr){
返回arr;
}
函数LinkedList(){}
LinkedList.prototype.push=函数(节点){
var l=this.tree.children.length;
var j=l-1;
如果(l==0){
this.tree.children.push(节点);
返回;
}
//反向遍历子数组以获取父数组
而(j>-1){
var d=this.tree.children[j].depth;
//孩子
如果(node.depth>d){
console.log(this.tree.children[j],节点)
返回此.tree.children[j].children.push(节点);
}
//兄弟姐妹
如果(node.depth==d){
}
j--;
}
}
功能分解(arr){
var treeBreak=/[\r\n](?=\w)/gm;
变量行=/[\r\n]+(?!\s)*/g;
返回arr.split(treeBreak).map(函数(str){
返回str.split(行)
});
}
功能$test(str){
var json=json.stringify(str,null,2);
var wtf=”“+json+”;
function populateTree (tree, text) {
    var rTab, rChunks, rChunk;
    var chunks, chunk;
    var i, l, node;
    if (!text) return;
    rTab    = /^\s{4}/gm;
    rChunks = /[\r\n]+(?!\s{4})/g;
    rChunk  = /^(.+)(?:[\r\n]+((?:\r|\n|.)+))?$/;
    chunks  = text.split(rChunks);
    l       = chunks.length;
    for (i = 0; i < l; i++) {
        chunk = chunks[i].match(rChunk);
        node = { label: chunk[1], children: [] };
        tree.children.push(node);
        populateTree(node, chunk[2] && chunk[2].replace(rTab, ''));
    }
}

function printTree(tree, prefix) {
    var i, l = tree.children.length;
    for (i = 0; i < l; i++) {
        console.log(prefix + tree.children[i].label);
        printTree(tree.children[i], prefix + '  ');
    }
}
文件编写(wtf); } 返回init; })($文本); dirGen();
到目前为止,代码为我提供了这个json数组:


我懒得读你的算法:-|

var tree = { children: [] };
populateTree(tree, text);
printTree(tree, '');
我不熟悉Nodejs,我只能说它在Chrome中使用以下字符串:

var treeGrapher = (function() {
  "use strict";

  var find = require("lodash.find");

  var indent = /[\s\t]/g;
  var lTrim = /[\s\t]*/;
  var treeBreak = /[\r\n](?=\w)/gm;
  var lines = /[^\r\n]+/g

  function init(text) {
    return decompose(text).map(function(tree) {
      return populate(-1, tree, {})
    });
  }

  function depth(str) {
    var d = str.match(indent);
    if (d === null) return 0;
    return d.length;
  }

  function decompose(txt) {
    return txt.split(treeBreak).map(function(str) {
      return str.match(lines);
    });
  }

  function populate(n, tree, root, cache, breadCrumbs) {
    var branch, leaf, crumb;
    //set index
    n++;
    //root case
    if (n === tree.length) return root.tree;

    branch = tree[n];
    leaf = { label: branch.replace(lTrim, ""), index: n, depth: depth(branch), children: [] };
    breadCrumbs = breadCrumbs || [];
    crumb = cache ? { label: cache.label, index: cache.index, depth: cache.depth, node: cache } : undefined;

    //set root
    if (n === 0) { 
      root.tree = leaf;
      return populate(n, tree, root, leaf, breadCrumbs);
    }

    //push child to cached parent from previous iteration
    if (leaf.depth > cache.depth) {
      cache.children.push(leaf);
      root.parent = cache;
      breadCrumbs.push(crumb)
      return populate(n, tree, root, leaf, breadCrumbs);
    }

    //push child to distant parent via breadcrumb search
    if (leaf.depth <= cache.depth) {
      var rev = breadCrumbs.slice(0).reverse();
      var parentNode = find(rev, function(obj){ return obj.depth < leaf.depth }).node;
      parentNode.children.push(leaf);
      return populate(n, tree, root, leaf, breadCrumbs);
    }

  }

  return init;

})();

module.exports = treeGrapher;
(回答我自己的问题)

好的,所以实现实际上有三个部分:(1)将文本文件转换为树结构,然后(2)在树上使用dfs查找唯一路径,最后(3)将所有路径合并到单个数组中

首先,文本到树的转换器。您仍然需要找到每个项目的深度(缩进级别),因为这决定了它是子项目还是同级项目:

var uniquePaths = (function() {
  "use strict";

  function init(tree) {
    return walk(tree, [], []);
  }

  function walk(branch, path, basket) {
    var fork = path.slice(0);
    var i = 0;
    var chld = branch.children;
    var len = chld.length;
    fork.push(branch.label);
    if (len === 0) { 
      basket.push(fork);
      return basket;
    }
    for (i; i < len; i++) walk(chld[i], fork, basket);
    return basket;
  }

  return init;

})();

module.exports = uniquePaths;
main.js


新结构的用途是什么?似乎可以对其进行改进,使其对递归循环更加友好。还有,是什么生成了文本文件?如果这是一个脚本,你可以访问你可能会杀死2鸟一个stone@charlietfl目标是拥有一个npm模块,当您初始化它时,它将查找目录模板,并通过加入算法生成的每个唯一路径数组的索引,自动
mkdirp
目录结构。如果这样做,为什么要写入文本文件?如果您不同时写入数组,并使嵌套数组更像一个传统的树结构,在每个级别上都有相同的子节点数组名称,这是没有意义的。可能我解释得不够好;模块使用
fs.readFileSync
搜索包含要使用的目录结构的文本文件。到目前为止,我编写的代码将文本文件转换为需要用于
fs.mkdirp(target,callback)
的数据,例如:
var uniquePath=[“root1”、“child1”、“孙子1”];mkdirp(uniquePath.join(“/”),函数(err,res){//directory created})查找此问题的两种通用算法是深度优先遍历和广度优先遍历。首先,这非常有用:)
root1
    child1
    child2
        gc1
root2
root3
    root3-child1    
var fs = require("fs");
var treeGrapher = require("./lib/treeGrapher.js");
var uniquePaths = require("./lib/uniquePaths.js");  

var tmpl = fs.readFileSync("./director.tmpl.txt", "utf8");
var graphs = treeGrapher(tmpl); //returns an array of trees
var paths = arrange(graphs);

/**    
[ 
    [ "root1", "rootchild1" ],
    [ "root1", "child2", "gc1" ],
    [ "root2" ],
    [ "root3", "root3-child1" ]
]
*/

function arrange(trees) {
    var bucket = [];
    trees.forEach(function(list) {
        uniquePaths(list).forEach(function(arr) {
            bucket.push(arr);
        });
    });
    return bucket;
}