树递归 - 如何在深度优先搜索中包含条件?

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案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 只需检查结束节点是否为叶子。请检查更新。 啊,我明白了。非常感谢!

以上是关于树递归 - 如何在深度优先搜索中包含条件?的主要内容,如果未能解决你的问题,请参考以下文章

二分搜索树的深度优先遍历和广度优先遍历

如何使用非递归方法实现深度优先搜索图

广度优先深度优先搜索算法——面试题

三分钟讲明白DFS(深度优先搜索)

二叉树深度优先遍历解题思路

binarytree二叉树节点DFS深度优先搜索遍历,递归,python