【问题标题】:Tree recursion - how to include conditions in depth-first search?树递归 - 如何在深度优先搜索中包含条件?
【发布时间】:2022-01-11 03:30:33
【问题描述】:

我有一棵树(非二进制、不平衡、无循环),所有节点都有标志(绿色=活动,红色=不活动)。我从根节点开始,我必须找到所有节点都处于活动状态的完整路径(从根到叶)。 (找到至少一条路径就可以了。)因此,我需要路径,而不仅仅是信息(如果有的话)。

我正在考虑使用深度优先搜索,但我不知道如何包含按活动/非活动进行的过滤。有什么想法吗?

【问题讨论】:

    标签: algorithm recursion tree depth-first-search


    【解决方案1】:

    假设

            A                              1
           / \                            / \
          B   C -- G                     2   3 -- 7
         / \   \    \        <=>        / \   \    \
        D   E   F    J                 4   5   6    10
       / \            \               / \            \
      H   I            K             8   9            11
    

    因此,我可以使用算法深度优先搜索为您的问题提供解决方案。

    /*
    A - 1,
    B - 2,
    C - 3,
    D - 4,
    E - 5,
    F - 6,
    G - 7,
    H - 8,
    I - 9,
    J - 10,
    K - 11.
    */
    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    using namespace std;
    const int maximumSize=20;
    int vertices, edges;
    vector<int> visited0(maximumSize, 0);
    vector<int> visited1(maximumSize, 0);
    vector<int> graph[maximumSize];
    vector<int> distances(maximumSize, 0);
    vector<string> graphPaths;
    string path;
    vector<int> green(maximumSize, 0);
    template<class Type>
    void showContent1D(Type& input)
    {
        for(int i=0; i<input.size(); ++i)
        {
            cout<<input[i]<<", ";
        }
        return;
    }
    void showContentVectorString(vector<string>& input)
    {
        for(int i=0; i<input.size(); ++i)
        {
            cout<<input[i]<<", ";
        }
        return;
    }
    void createGraph()
    {
        cin>>vertices>>edges;
        int vertex0, vertex1;
        for(int i=1; i<=edges; ++i)
        {
            cin>>vertex0>>vertex1;
            graph[vertex0].push_back(vertex1);
            graph[vertex1].push_back(vertex0);
        }
        for(int i=1; i<=vertices; ++i)
        {
            cin>>green[i];
        }
        return;
    }
    void dfs0(int current, int previous)
    {
        if(visited0[current]==1)
        {
            return;
        }
        visited0[current]=1;
        distances[current]=0;
        for(int next : graph[current])
        {
            if(next==previous)
            {
                continue;
            }
            dfs0(next, current);
            distances[current]=max(distances[current], distances[next]+1);
        }
        return;
    }
    void dfs1(int root, int current, int previous)
    {
        if(visited1[current]==1)
        {
            return;
        }
        visited1[current]=1;
        if(green[current]==1)
        {
            if(distances[current]!=0)
            {
                path.append(to_string(current));
                path.append("->");
            }
            else
            {
                path.append(to_string(current));
                graphPaths.push_back(path);
                path.pop_back();
            }
        }
        for(int next : graph[current])
        {
            if(next==previous)
            {
                continue;
            }
            dfs1(root, next, current);
        }
        if(root==previous)
        {
            path.clear();
            path.append(to_string(root));
            path.append("->");
        }
        return;
    }
    void solve()
    {
        createGraph();
        dfs0(1, 0);
        dfs1(1, 1, 0);
        cout<<"graphPaths: ";
        showContentVectorString(graphPaths);
        cout<<endl;
        return;
    }
    int main()
    {
        solve();
        return 0;
    }
    

    输入:

    11 10
    1 2
    1 3
    2 4
    2 5
    4 8
    4 9
    3 6
    3 7
    7 10
    10 11
    1
    1
    1
    1
    0
    0
    1
    0
    1
    1
    0
    

    结果如下:

    graphPaths: 1->2->4->9, 
    

    如果您需要解决方案的解释,请写下相应的评论。

    【讨论】:

      【解决方案2】:

      您的 DFS 递归将有两种基本情况:

      • 负一:当前节点不是绿色的。
      • 肯定的:当前节点是一片绿叶,即它没有子节点。

      在所有其他情况下,必须对节点的子节点进行递归调用。一旦递归调用返回肯定结果,该肯定结果就可以用当前节点扩展并立即返回,从而中断循环。

      实现树的方式有多种,所以我在这个 JavaScript 实现中做了一些选择:

      function findGreenPath(tree, label) {
          let root = tree[label];
          if (!root.green) return null; // No path through none-green node
          if (root.children == "") return label; // It is a leaf, start a path
          for (let child of root.children) {
              let path = findGreenPath(tree, child);
              if (path != null) return label + path; // prepend this node to a good path
          }
          return null; // No path found
      }
      
      // Implementation of the example tree in the question:
      let tree = { // Dictionary of nodes by their label
          "A": {green: true, children: "BC"},
          "B": {green: true, children: "DE"},
          "C": {green: true, children: "FG"},
          "D": {green: true, children: "HI"},
          "E": {green: false, children: ""},
          "F": {green: false, children: ""},
          "G": {green: true, children: "J"},
          "H": {green: false, children: ""},
          "I": {green: true, children: ""},
          "J": {green: true, children: "K"},
          "K": {green: false, children: ""}
      };
      
      let path = findGreenPath(tree, "A"); 
      
      console.log(path); // ABDI

      【讨论】:

        【解决方案3】:

        这很简单。如您所知,DFS 可以通过堆栈来实现。这样我们将树的根压入堆栈,然后弹出堆栈顶部并压入弹出节点的子节点。我们继续这个过程直到有一个空堆栈。

        现在,对于您的情况,在将节点推入堆栈之前,您需要检查指定节点(即弹出节点的子节点)是活动的还是非活动的。在这种情况下,您不会在到达非活动节点时向下搜索。最后,只报告所有生成的路径,它们的结束节点是叶子(您可以在搜索过程中轻松找到叶子,一个没有任何子节点的节点)。

        【讨论】:

        • 但是我如何避免得到 'A-B-D' 的结果(假设首先检查 A-B-D-H,并且 H 因为不活动而被忽略)?
        • @Misa 只需检查结束节点是否为叶子。请检查更新。
        • 啊,我明白了。非常感谢!
        猜你喜欢
        • 1970-01-01
        • 2015-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多