【问题标题】:Algorithm for finding equal paths in two trees在两棵树中找到相等路径的算法
【发布时间】:2020-10-11 02:33:29
【问题描述】:

有两棵树。树的每个节点都包含一个值,该值可以与另一棵树的另一个节点的值进行比较。如果值相等,则节点相等。

每个节点可以包含从 0 到任意数量的子节点。

需要判断是否可以在这两棵树中建立相等的路径?这些路径上的节点必须相等。两条路径都应该从顶部到包含 0 个子节点的节点。沿路径的节点数当然也应该相等。

例子:

  a           a        a
 /|\         /|\      / \
b c d       1 d z    x   y
| | |\      | |      |
e f g h     x g      s

前两棵树的路径“adg”相等。第三棵树与前两棵树的路径不相等。

有现成的算法来解决这样的问题吗?如果存在,怎么称呼它,从哪里可以读到?

【问题讨论】:

  • 您是否正在寻找比使用 DFS 为每棵树生成所有根到叶路径然后与这些集合相交更有效的方法?
  • 生成一个集合,其中所有字符串表示较小树中的路径,然后在第二棵树中生成单词并检查每个单词是否在集合内。
  • 节点中的值是否不同?
  • 没有。在同一棵树中可以有多个具有相同值的节点。
  • 但是同一个节点的所有子节点都是唯一的。

标签: javascript c++ algorithm tree tree-traversal


【解决方案1】:

解决方案涉及找到一个交集树

要找出两棵树之间的交集树,我们需要将两棵树的每个节点的子节点相交。

假设您有树 AB,其根为 ab。我们的目标是构建一个交叉树C

如果a ≠ b,则C 为空。

否则,让C 的根ca

c 的子代将是 children(a) ∩ children(b)

现在,对于c 的每个子代cᵢcᵢ 的子代将是ABcᵢ 的子代的交集。重复此操作,直到交集树完成。

表示树的一个好方法是使用unordered_map<char, node> 并使用unordered_set 来表示节点的子节点并快速找到共同的子节点。

这是一个工作示例:

#include <bits/stdc++.h>
using namespace std;

/*

  a           a   
 /|\         /|\   
b c d       1 d z  
| | |\      | |      
e f g h     x g     

*/


struct node{
    char value;
    unordered_set<char> children;
    node(){}
    node(char v){value = v; children.clear();}
    void add(char c){children.insert(c);}
};

unordered_map<char, node> A, B, C; //trees

void intersection_tree(char ra){  //method that generates C recursively
    
    auto ita = A.find(ra);
    auto itb = B.find(ra);
    
    if(ita == A.end() || itb == B.end()) return;
    
    node a = ita->second, b = itb->second;
    
    if(a.value == b.value){
        node n(a.value);
        
        vector<char> intersection;
        for (const char& elem: a.children) {
            if(b.children.find(elem) != b.children.end()){
                intersection.push_back(elem);
                n.add(elem);
            }
            C[a.value] = n;
        }
        for(int i = 0; i < intersection.size(); i++){
            intersection_tree(intersection[i]);
        }
    }
}

void dfs(char c, string s = ""){  //dfs to print common paths
    node n = C[c];
    if(n.children.size() == 0) cout<<(s + c)<<endl;
    for (const char& elem: n.children) {
        dfs(elem, s + n.value);        
    }
}


int main(){
    //fill A
    node Aa('a'); Aa.add('b'); Aa.add('c'); Aa.add('d');
    node Ab('b'); Ab.add('e');
    node Ac('c'); Ac.add('f');
    node Ad('d'); Ad.add('g'); Ad.add('h'); 
    node Ae('e'); 
    node Af('f'); 
    node Ag('g'); 
    node Ah('h');
    A['a'] = Aa; A['b'] = Ab; A['c'] = Ac; A['d'] = Ad; A['e'] = Ae; A['f'] = Af; A['g'] = Ag; A['h'] = Ah;
   
    //fill B 
    node Ba('a'); Ba.add('1'); Ba.add('d'); Ba.add('z');
    node B1('1'); B1.add('x');
    node Bd('d'); Bd.add('g');
    node Bz('1');
    node Bx('x');
    node Bg('g');
    B['a'] = Ba; B['1'] = B1; B['d'] = Bd; B['z'] = Bz; B['x'] = Bx; B['g'] = Bg; 
    
    intersection_tree('a');   //generate C
    
    dfs('a');                 //print common paths

    return 0;
}

输出:adg

这个方法的复杂度是O(n)。

【讨论】:

    【解决方案2】:

    正如其他人所说,您需要获取给定树的交叉点,但附加条件是交叉路径应以两棵树的叶子结尾。

    您可以通过串联遍历两棵树来做到这一点。这可以是深度优先遍历。

    我将假设节点值不必是唯一的。为了避免一个节点的子节点必须与另一个节点的所有子节点进行比较,兄弟节点可以按它们的值排序。这样,两个节点的子节点就可以在线性时间内进行比较。由于排序,整个算法的运行时间为O(nlogn)。如果值在单个树中是唯一的,则可以通过它们的值对子节点进行散列,从而使算法 O(n)。但如前所述,我将选择允许重复值的变体:

    这是一个 JavaScript 实现:

    class Node {
        constructor(value, ...children) {
            this.value = value;
            // sort the children by their value
            this.children = children.sort();
        }
        
        commonPaths(node) {
            if (this.value !== node.value) return [];
            if (this.children.length + node.children.length == 0) return [this.value];
            let result = [], i = 0, j = 0;
            while (i < this.children.length && j < node.children.length) {
                let a = this.children[i], b = node.children[j];
                for (let path of a.commonPaths(b)) result.push([this.value, ...path]);
                i += (a.value <= b.value); // boolean converts to 0 or 1
                j += (a.value >= b.value);
            }
            return result;
        }
        toString() { return this.value }
    }
    
    // Create the trees given in the question
    let tree1 = new Node("a",
        new Node("b", new Node("e")),
        new Node("c", new Node("f")),
        new Node("d", new Node("g"), new Node("h"))
    );
    
    let tree2 = new Node("a",
        new Node("1", new Node("x")),
        new Node("d", new Node("g")),
        new Node("z")
    );
    
    let tree3 = new Node("a", 
        new Node("x", new Node("s")),
        new Node("y")
    );
    
    // Get common paths of pairs of these trees
    console.log(tree1.commonPaths(tree2));
    console.log(tree1.commonPaths(tree3));

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多