1.图的结构
图会包括点的集合与边的集合,然后单独去构建出点和边的类
所以图一共会联系三个类 分别为Node点类,Edge边类,Graphic图类
class Node;
class Edge;
class Graph;
class Edge
{
public :
int weight;//权值
Node* from;//这个边的初始点
Node* to;//这个边的终点
}
class Node
{
public :
int val; //这个点的值,可以是string,或其他类型
int in; //入度
int out; //出度
list<Node*> nexts; // 所有与这个点相连的下一个点
list<Edge*> edges; //所有与这个点有联系的边
Node(int val) : val(val)
{
in = 0;
out = 0;
nexts = {};
edges = {};
}
}
class Graph
{
public :
//int为对应的值,可以是string
unordered_map<int, Node*> m_map;
unordered_set<Edge*> m_set;
Graph()
{
m_map = {};
m_set = {};
}
}
2.将给定结构转为这个结构
比如给一个二维数组,其中0代表权值,1代表出发点,2代表目的点
{
{1,2,3}
{4,5,6}
}
,将其转化为Graph结构
Graph* Transformation(const vector<vector<int>> &v)
{
//先构建点,然后构建边,然后将边和点都加入到我们万能Graph中
Graph* graph = new Graph();
for(int i = 0; i < v.size(); i ++)
{
int weight = v[i][0], from = v[i][1], to = v[i][2];
//构建点Node并将Node加入到graph中
if(graph->m_map.count(from) == 0) graph->m_map[from] = new Node(from);
if(graph->m_map.count(to) == 0) graph->m_map[to] = new Node(to);
//构建边
Node* from = graph->m_map[from];
Node* to = graph->m_map[to];
Edge* edge = new edge(weight, from, to);
//if(graph->m_set.count(edge) == 0)
graph->m_set.insert(edge);
//出入度以及边集的调整
from->out ++;
to->in ++;
from->nexts.push_back(to);
}
return graph;
}
3.广度遍历,给定某个图的某个节点,以这个节点出发,广度遍历这个图
广度遍历是先将与这个点的相邻的点都先打印一遍,在打印的过程中,要加入其它点连接的点
首先将A打印,然后打印A的下一个节点BCD,在打印B的情况下要记录E的位置,之后在打印完BCD之后打印E。如何记录E的位置,队列先进先出就比较符合,先将A加入队列,然后将ABC加入队列,然后再将E加入队列即可。
如何保证在打印C的时候不重复将B加入队列呢(因为是找的与C相邻的点,所以可能在打印C时加入B)?可以用一个哈希表来辅助。每次打印完一个节点后,就将其加入到这个哈希表中,在将节点加入队列前先判断这个节点是否已经在哈希表中了,如果在的话跳过即可。
void DFS(Node* node)
{
//创建一个队列,以及一个set,其中set用于记录某个节点是否添加到过队列中
queue<Node*> qu;
unordered_set<Node*> se;
qu.emplace(node);
se.emplace(node);
while(!qu.empty()) //队列如果不空,那么就将队列首个元素弹出,并且记录,然后遍历这个元素的所有nexts,如果里边的元素没有存在set中,证明还没加入过队列
{
Node* temp = qu.front();
cout << temp->val << " "; //出队打印即可
for(auto i : temp->nexts)
{
if(se.count(i) == 0)//证明还没有加入过队列
{
se.emplace(i);
qu.emplace(i);
}
}
}
}
4.深度优先遍历
深度优先遍历是找到某一个点,然后一直向下打印,直到这个点已经找不到下一个点了,然后找另一条路,直到没有路可走了。
在这里就是打印A,从A找一条路,可以是B或C或D,这里找B,B下面可以是E或C,这里选C,然后接着走是D,之后发现没路了,回退一个,发现C也没路了,继续回退到B,B发现还有E,打印E,然后E没路了,回退到B,B没路了回退到A,A发现也没路了,因为C和D都已经打印过了,A没办法回退了,直接结束
如何做到回退,可以用栈去记录节点,比如打印ABCD这条路,我们压栈ABCD,D继续发现没路了,将D弹出,还能得到C,C没路了弹出得到B,B找到E将E入栈,变为ABE,之后ABE都被弹出,栈空了直接返回就行
同时用一个哈希表记录是否已经打印过了
void DFS(Node* node)
{
//深度优先遍历需要一个栈去记录当前打印的顺序,set仍然是记录Node是否进入过栈
stack<Node*> st;
unordered_set<Node*> se;
st.emplace(node);
cout << st.top()->val << " ";//进栈打印
se.emplace(node);
while(!st.empty())
{
//首先存储栈顶的Node
Node* temp = st.top();
st.pop();
for(auto i : temp->nexts())
{
if(se.count(i) == 0) //证明还没有加入过栈中
{
st.emplace(temp);
st.emplace(i);
cout << st.top()->val << " ";//进栈打印
se.emplace(i);
break;
}
}
}
}