我正在重新学习图论,我想编写一个Graph类,允许我实现这些方法。是否有更好的实现通用Graph类的方法?
主要职能:
int main() {
using namespace GraphUtils;
Graph<char> graph;
Node<char> a('a');
Node<char> b('b');
Node<char> c('c');
Node<char> d('d');
Node<char> e('e');
Node<char> f('f');
graph.AddNode(a).AddNode(b).AddNode(c).AddNode(d).AddNode(e).AddNode(f);
a.AddChild(f).AddChild(d);
b.AddChild(c).AddChild(a);
c.AddChild(d);
e.AddChild(f);
f.AddChild(b);
graph.Print();
if( !PathExist(graph, a, b)) {
cout << "ERR: a -> b exists" << endl;
}
if( !PathExist(graph, a, c)) {
cout << "ERR: a -> c exists" << endl;
}
if( PathExist(graph, a, e)) {
cout << "ERR: a -> c doesn't exist" << endl;
}
if( PathExist(graph, c, e)) {
cout << "ERR: c -> e doesn't exist" << endl;
}
if( !PathExist(graph, e, c)) {
cout << "ERR: e -> c exists" << endl;
}
}类实现,它严重地面向指针:
#include <iostream>
#include <map>
#include <vector>
#include <queue>
using std::map;
using std::vector;
using std::cout;
using std::endl;
using std::queue;
namespace GraphUtils {
template <typename T>
struct Node {
T data;
vector<Node*> children;
Node() {}
Node(T input): data(input) {}
Node& AddChild(Node& input) {
children.push_back(&input);
return *this;
}
};
template <typename T>
bool operator<(Node<T>& lhs, Node<T>& rhs) {
return (lhs.data < rhs.data);
}
template <typename T>
bool operator==(Node<T>& lhs, Node<T>& rhs) {
return !(lhs.data < rhs.data || lhs.data > rhs.data);
}
template<typename T>
struct Graph {
vector<const Node<T>* > nodes;
Graph& AddNode(const Node<T>& input) {
nodes.push_back(&input);
return *this;
}
void Print() {
for(typename vector<const Node<T>* >::const_iterator iter = nodes.begin();
iter != nodes.end();
iter++)
{
cout << (*iter)->data << ":";
for(typename vector<Node<T>* >::const_iterator child = (*iter)->children.begin();
child != (*iter)->children.end();
child++)
{
cout << (*child)->data << ", ";
}
cout << endl;
}
}
};
// using BFS to determine if path exists
template<typename T>
bool PathExist(Graph<T>& graph, Node<T>& a, Node<T>& b) {
map<const Node<T>* , bool> t_visited;
queue<Node<T>* > t_queue;
for(typename vector<const Node<T>* >::const_iterator iter = graph.nodes.begin();
iter != graph.nodes.end(); iter++)
{
t_visited[*iter] = false;
}
t_queue.push(&a);
Node<T>* t_node;
while(!t_queue.empty()) {
// get the top object
t_node = t_queue.front();
t_queue.pop();
// set visited
t_visited[t_node] = true;
// node doesn't have the == operator
if(*t_node == b) return true;
// for each ot it's children
for(typename vector<Node<T>* >::iterator iter = t_node->children.begin();
iter != t_node->children.end(); iter++)
{
// if item has not been visited
if(!t_visited[(*iter)]) {
t_queue.push((*iter));
}
}
}
return false;
}
};发布于 2015-08-06 02:52:54
这在某种意义上取决于什么是好的。这是一个非常标准的邻接列表实现,这是没有错的。它是否好取决于您将询问哪个查询的数据结构。如果对稀疏图进行路径搜索或最小生成树,那是很好的。如果你想做一些聚类,那么也许邻接矩阵更好。
在API级别上,我喜欢链式添加方法。它很好,而且很容易使用。存储指向别名的指针避免了内存泄漏的问题,但它使程序员觉得为每个节点声明一个变量有点傻,其中只有一个字符。如果节点的数量动态增长,这是使用邻接列表的优势之一,那该怎么办?为什么我不能喜欢graph.AddNode('a')?然后用graph.get('a').AddChild('b')或其他什么方式添加子元素。
此外,如果节点局部变量超出作用域,则该图形必须死掉或面临分割错误的危险,甚至是一些安全问题。不太灵活。
发布于 2015-08-09 21:01:57
不是的。您基本上允许入侵实现细节来实现任何事情。
struct Graph {
vector<const Node<T>* > nodes;基本上,您将给自己带来麻烦,因为您已经公开了图形如何存储其数据的实现细节。现在人们不得不用这个来编写他们的函数。
锁定成员变量,并通过添加节点和边缘的方法提供对类的访问。
您还应该查找访问者模式。这允许人们编写图遍历算法,而不知道图的细节。
usingusing std::map;
using std::vector;
using std::cout;
using std::endl;
using std::queue;您将通过将其放入头文件来破坏其他人的代码。我不喜欢你在源文件中使用这个(除非你仔细观察它)。但将其放入头文件是一个定义不-否。如果我将头文件包含在源文件中,则可能会导致代码中几乎无法检测到的破坏。因此,您将发现大多数项目将拒绝使用您的代码,因为他们的代码库潜在的危险。
如果你不介意把你自己的代码搞乱(显然你不介意),把它放在你自己的命名空间里,那么至少它不会影响我的代码。
正确地使用它们。
Graph& AddNode(const Node<T>& input) {
nodes.push_back(&input);
return *this;
}因此,您可以通过引用传递节点。这意味着你会在内部复制一份。这使得这是多余的:
Node<char> a('a');
Node<char> b('b');
Node<char> c('c');
Node<char> d('d');
Node<char> e('e');
Node<char> f('f');
graph.AddNode(a).AddNode(b).AddNode(c).AddNode(d).AddNode(e).AddNode(f);现在每个节点都有两个副本。
这样写会更容易:
graph.AddNode(Node<char>('a')).AddNode(Node<char>('b')).AddNode(Node<char>('c')).AddNode(Node<char>('d')).AddNode(Node<char>('e')).AddNode(Node<char>('f'));但这似乎有点冗长。因此,您可以编写一个帮助函数,为您进行类型推断。
template<typename T>
Node<T> mkNode(T const& v) {return Node<T>(v);}
graph.AddNode(mkNode('a')).AddNode(mkNode('b')).AddNode(mkNode('c')).AddNode(mkNode('d')).AddNode(mkNode('e')).AddNode(mkNode('f'));或者更好的是,只需编写一个AddNode(),它需要一个T,internall就可以创建一个正确类型的Node。
graph.AddNode('a').AddNode('b').AddNode('c').AddNode('d').AddNode('e').AddNode('f');https://codereview.stackexchange.com/questions/99153
复制相似问题