测试一个图是否是树

Testing whether a graph is a tree

本文关键字:是否是 一个 测试      更新时间:2023-10-16

输入规范N给出节点个数,M给出边个数。第一个简单的检验是M应该等于N-1否则它就不可能是树。

我接下来做的只是一个DFS,我看到在DFS期间,我们是否再次遇到访问过的节点(与父节点不同,这里的父节点是指调用相邻节点的DFS的节点),那么这意味着我们有一个循环,它不是树。但显然我的解决方案总是得到错误的答案。我张贴的代码,但只有片段是重要的。我将图存储为邻接表,并发布函数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 << "NOn";
    else if (G.isTree())
        cout << "YESn";
    else
        cout << "NOn"; 
}

我编译了你的代码,并在我的机器上运行。在实现图形时,需要考虑一些重要的规范。当您选择遵守规范时,在代码中强制执行该规范通常是一种好做法。

图有双向边已经很清楚了,尽管特别提到这一点也无妨。

允许重复边?你的程序允许我先画一条边(1,2)然后再画一条边(1,2)然后算为两条边。这使得条件M != N-1成为一个不充分的检查。要么禁止重复的边,要么在你的算法中考虑它们(目前,重复的边会导致你的算法返回不正确)。

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

为了帮助你,这里是我修改的addEdge()的实现,它禁止重复的边和禁止自循环。作为奖励,它还检查数组边界;)请注意附加的include和函数签名的变化(它现在返回bool值)。

#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++;
    }
}

它可以运行我在纸上画的所有简单的测试用例,但是当提交时它失败了!

听起来像是家庭作业的自动评分系统,对吗?如果您可以访问准确的测试用例,那么问题将是显而易见的。在这种情况下,它可能是不可用的,所以我们只能推测。

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

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

也就是说,你的程序是否准备为此返回"NO"?

     _
    / 
o  o---o
Nodes: 3, edges: 2
相关文章: