图的常见表示方法是邻接表和邻接矩阵,但是没了应对题目的千变万化和降低考试的时候的错误率,可以自己写一种自己熟悉的图结构,然后再遇到图算法的题目的时候,只需要写一个转化函数将题目给的图结构转换为自己定义的图结构就行
左神课程给出的图结构模板
左神语录:不带模板上考场,就等于裸奔
构建思路
图模板
1) 节点类:包含节点值,入度值,出度值,与该节点相邻的节点集合(表示该节点可以通过边指向谁),该节点出去的边的集合
2)边类:包含边的权重,边的出发节点和变得终止节点
a) 图的大结构
class Node
{
public:
int value;//节点的值,可以是任何类型,表示该节点存放的内容
int in;//入度
int out;//出度
list<Node*> nexts;//与节点相邻的节点集合
list<Edge*> edges;//属于节点的边的集合,表示那个边是从该节点出去的
Node(int v)
{
value = v;
in = 0;
out = 0;
}
Node() { value = 0; in = 0; out = 0; }
};
class Edge
{
public:
int weight;
Node* from;
Node* to;
Edge(int w, Node* N1, Node* N2)//因为需要定义Node这个类,所以需要再从类中加上
//一个没有接受参数的构造函数,不然会出问题
{
weight = w;
from = N1;
to = N2;
}
//Edge() { weight = 0, from = nullptr; to = nullptr; }
};
class GraphF
{
public:
unordered_map<int, Node*> nodes;//点集,第一个参数表示第几个节点
unordered_set<Edge*> edges;//边集
GraphF() {};//用于定义GraphF类对象
};
图结构转换函数
- 图结构之间的转换
给定一个二维数组matrix,shape=[N,3];第一列表示边的出发节点,第二列表示点的终止节点,第三列表示边的权重;比如【0,3,5】表示节点0指向节点3,边的权重为5; - code
#include<iostream>
#include<unordered_map>
#include<unordered_set>
#include<list>
#include<vector>
using namespace std;
class Edge;
class Node
{
public:
int value;//节点的值,可以是任何类型,表示该节点存放的内容
int in;//入度
int out;//出度
list<Node*> nexts;//与节点相邻的节点集合
list<Edge*> edges;//属于节点的边的集合,表示那个边是从该节点出去的
Node(int v)
{
value = v;
in = 0;
out = 0;
}
Node() { value = 0; in = 0; out = 0; }
};
class Edge
{
public:
int weight;
Node* from;
Node* to;
Edge(int w, Node* N1, Node* N2)//因为需要定义Node这个类,所以需要再从类中加上
//一个没有接受参数的构造函数,不然会出问题
{
weight = w;
from = N1;
to = N2;
}
//Edge() { weight = 0, from = nullptr; to = nullptr; }
};
class GraphF
{
public:
unordered_map<int, Node*> nodes;//点集,第一个参数表示第几个节点
unordered_set<Edge*> edges;//边集
GraphF() {};//用于定义GraphF类对象
};
/*
[
[0,1,7],
[1,2,5],
[2,3,7],
[3,1,8]
]
第一列表示出发节点
第二列表示结尾节点
第三列表示两个节点直接边的权重
*/
//将上边的图结构描述转换为我们定义的图结构描述
GraphF trans_Graph(vector<vector<int>>& matrix)
{
GraphF graph;
for (int k = 0; k < matrix.size(); k++)
{
int from = matrix[k][0];
int to = matrix[k][1];
int weight = matrix[k][2];
Node newfromNode(from);
Node newtoNode(to);
if (graph.nodes.find(from) == graph.nodes.end())
graph.nodes.insert(pair<int, Node*>(from, &newfromNode));
if (graph.nodes.find(to) == graph.nodes.end())
graph.nodes.insert(pair<int, Node*>(to, &newtoNode));
auto fromNode = graph.nodes.find(from);
auto toNode = graph.nodes.find(to);
(*fromNode).second->nexts.push_back(toNode->second);
(*fromNode).second->out++;
(*toNode).second->in++;
Edge new_edge(weight, (*fromNode).second, (*toNode).second);
Edge* newedge = &new_edge;
(*fromNode).second->edges.push_back(newedge);
graph.edges.insert(newedge);
}
return graph;
}
int main()
{
vector<vector<int>> mat{ {0,1,7},{1,2,5},{2,3,7},{3,1,8} };
GraphF G;
G = trans_Graph(mat);
cout << 1 << endl;
return 0;
}
代码地址:https://github.com/hjfenghj/cpp_code/blob/main/graph_tran.cpp
问题记录
再写以上代码的时候,出现了蛮多错误,总结起来就三点
- 以上有两个类Node和Edge,而且两者互相调用,就需要有个先后;
类或结构体的前向声明只能用来定义指针对象或引用,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象,所以会报错;所以如果把类Edge提前声明,那么再Node中只能定义Edge的指针,否者会出现field has incomplete type - 注意pair结构的使用方法
- 根据类的使用方法,添加对应的构造函数;比如需要想定义整型变量那样定义一个类对象,就需要加一个无参数的构造函数;
下一篇来纪录几道使用图模板的习题