【问题标题】:Testing whether a graph is a tree测试图是否为树
【发布时间】:2014-08-06 03:01:29
【问题描述】:

在输入规范中,N 是节点数,M 是边数。所以第一个简单的检查是 M 应该等于 N-1 否则它根本不可能是一棵树。

我接下来所做的只是一个 DFS,在其中我看到在 DFS 期间我们是否再次遇到访问过的节点(与父节点不同,父节点是指调用下一个 dfs 的节点与它相邻的节点)那么这意味着我们有一个循环,它不是一棵树。但显然我的解决方案不断得到错误的答案。我发布了代码,但只发布了重要的 sn-ps。我将图形存储为邻接列表,我发布了函数isTree(),它测试它是否是一棵树?什么是正确的逻辑?

#include <iostream>
#include <list> 
using namespace std;

    // Graph class represents a directed graph using adjacency list representation
class Graph
{
    int V;    // No. of vertices
    list<int> *adj;    // Pointer to an array containing adjacency lists
    bool isTreeUtil(int v, bool visited[],int parent);
public:
    Graph(int V);   // Constructor
    void addEdge(int v, int w);   // function to add an edge to graph
    bool isTree();   // Tells whether the given graph is a tree or not
    void printGraph();
};

Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V+1];
}

void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w); // Add w to v’s list.
    adj[w].push_back(v);
}

bool Graph::isTreeUtil(int v, bool visited[],int parent)
{
    //int s_v = v;
    visited[v] = true;
    list<int>::iterator i;
    for(i = adj[v].begin(); i != adj[v].end(); ++i) {
        if (!visited[*i])
            isTreeUtil(*i,visited,v);
        else {
            if (*i != parent && visited[*i])
                return false;
        }
    }

    return true;
 }

bool Graph::isTree() {
    bool *visited = new bool[V+1];
    for(int i = 1; i < V+1; i++)
        visited[i] = false;

    visited[1] = true;  // marking the first node as visited
    for(int i = 1; i < V+1; i++)
        visited[i] = false;
    int parent = -1;   // initially it has no parent
    //list<int> :: iterator i;
    //for (i = adj[v].begin(); i != adj[v].end(); ++i)
    return isTreeUtil(1, visited, parent);
}

void Graph::printGraph() {
    for (int i = 1;i <= this->V; i++) {
        cout << i << "->";
        list<int>::iterator j;
        for (j = adj[i].begin(); j != adj[i].end(); ++j) {
            cout << *j << "->";
        }
        cout << "\n";
    }
}

int main() {
    int N, M;
    cin >> N >> M;

    Graph G(N);
    int v, w;
    int m = 0;
    while (m < M) {
        cin >> v >> w;
        G.addEdge(v,w);
        m++;
    }

    if (M != N-1) {
        cout << "NO\n";
    else if (G.isTree())
        cout << "YES\n";
    else
        cout << "NO\n"; 
}

【问题讨论】:

  • 为什么要投反对票?我把问题写清楚了
  • 我没有投反对票(以防万一)
  • 您提交的代码要求用户输入以填充图表。这很乏味,您能否提供一些对于 isTree() 得到错误结果的图表示例?
  • @TobyLiu 这就是我在纸上绘制的所有简单测试用例的问题所在,但提交失败时!而且我不知道哪个测试用例失败了。
  • 树是图,但任意图不一定是树。

标签: c++ graph tree depth-first-search


【解决方案1】:

我获取了您的代码,编译并在我的机器上运行它。在实现图表时,需要考虑一些重要的规范。当您选择遵守规范时,通常最好在代码中强制执行该规范。

很明显,该图具有 2 路边,尽管特别提及这一点并没有什么坏处。

允许重复边? 您的程序允许我制作边 (1,2),然后制作另一边 (1,2) 并将其计为 2 条边。这使您的条件 M != N-1 成为不充分的检查。要么禁止重复边,要么在你的算法中考虑它们(目前,重复边会导致你的算法错误地返回)。

自边缘? 你的图是否允许一个顶点对自己有一条边?如果是这样,自路径是否应该使树无效(也许自循环是合法的,因为在树中,每个节点都可以访问自己)?目前,自边缘也会破坏您的算法。

为了帮助您,这是我修改后的 addEdge() 实现,它不允许重复边并不允许自循环。作为奖励,它还检查数组边界;) 请注意额外的包含,以及函数签名的变化(它现在返回一个布尔值)。

#include <algorithm>

bool Graph::addEdge(int v, int w)
{
    // sanity check to keep us from seg faulting
    if (v < 1 || v > this->V || w < 1 || w > this->V) {
        return false;
    }
    // no self-edges
    if (w == v) {
        return false;
    }
    // no duplicate edges allowed either
    std::list<int>::iterator findV = std::find(adj[v].begin(), adj[v].end(), w);
    std::list<int>::iterator findW = std::find(adj[w].begin(), adj[w].end(), v);
    if (findV != adj[v].end() || findW != adj[w].end()) {
        return false;
    }

    adj[v].push_back(w); // Add w to v’s list.
    adj[w].push_back(v);
    return true;
}

我希望这会有所帮助。如果这是一项作业,您应该查看文章。如果您的实施是自动评分的,他们必须指定这些情况。正如@congusbongus 提到的,在节点断开连接的情况下,您的算法也会失败。

请注意,您还必须修改 main() 方法才能使我的实现正常工作。修改这部分函数:

while (m < M) {
    cout << "Create Edge from x to y" << endl;
    cin >> v >> w;
    if (!G.addEdge(v,w)) {
        cout << ">>Invalid edge not added" << endl;
    } else {
        cout << ">>Successfully added edge" << endl;
        m++;
    }
}

【讨论】:

  • 非常感谢,我恢复了对自己的信心。我知道我的逻辑是正确的,但不知何故,解决方案没有被接受。这种自循环事物和平行边缘,因为他们在问题规范中没有提到,我认为他们不会提供这种情况是理所当然的。非常感谢托比 :) !
【解决方案2】:

它适用于我在纸上绘制的所有简单测试用例,但提交时失败!

听起来像是一些家庭作业的自动标记系统,对吧?如果您可以访问确切的测试用例,那么问题将很明显。在这种情况下它可能不可用,所以我们只能推测。

根据我的经验,大多数此类失败都是由于错过了边界情况。您说检查边数 = 节点数 - 1,但您是否也考虑过以下问题?

  • 所有节点都已连接
  • 每对节点不超过一条边

也就是说,您的程序是否准备好为此返回“NO”?

     _
    / \
o  o---o

Nodes: 3, edges: 2

【讨论】:

  • 我明白你的意思,我的代码为此返回了一个是。所以我也必须检查平行边
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 2015-05-16
  • 2012-03-27
  • 2018-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多