图(graph)是用线连接在一起的顶点或节点的集合,即两个要素:边和顶点。每一条边连接个两个顶点,用(i,j)表示顶点为 i 和 j 的边。
如果用图示来表示一个图,一般用圆圈表示顶点,线段表示边。有方向的边称为有向边,对应的图成为有向图,没有方向的边称为无向边,对应的图叫无向图。对于无向图,边(i, j)和(j,i)是一样的,称顶点 i 和 j 是邻接的,边(i,j)关联于顶点 i 和 j ;对于有向图,边(i,j)表示由顶点 i 指向顶点 j 的边,即称顶点 i 邻接至顶点 j ,顶点 i 邻接于顶点 j ,边(i,j)关联至顶点 j 而关联于顶点 i 。
对于很多的实际问题,不同顶点之间的边的权值(长度、重量、成本、价值等实际意义)是不一样的,所以这样的图被称为加权图,反之边没有权值的图称为无权图。所以,图分为四种:加权有向图,加权无向图,无权有向图,无权无向图。
图的表现有很多种,邻接表法,临接矩阵等。
图经常是以这种形式出现的[weight,from,to]的n*3维数组出现的,见名知意,三个元素分别为边的权重,从哪儿来,到哪儿去。
本文以一个包含全部结点和边的对象来描述加权有向图。
其他三种稍微改下即可。
以下为结点类
class Node{
public:
Node(int val){
value=val;
in=0;
out=0;
}
int value;
int in; //入度,有多少个边指向此结点
int out; //出度,有多少条边由此结点发散
list<Node*> nexts; //此结点是from的情况下,邻居结点
list<Edge*> edges; //从此结点出发,发散出边的集合
};
边类
class Edge
{
public:
int weight;
Node* from;
Node* to;
Edge();
Edge(int _weight,Node* _from,Node* _to)
{
weight=_weight;
from=_from;
to=_to;
}
};
图类
class Graph{
public:
map<int,Node*> nodes;// 点序号和结点的映射集合
set<Edge*> edges; //边的集合
};
以下为创建一个图:
Graph* CreateGraph(int matrix[][3],int n) //n表示边数 即matrix.size()
{
Graph* graph=new Graph;
for(int i=0;i!=n;i++)
{
int weight=matrix[i][0];
int from=matrix[i][1];
int to=matrix[i][2];
if(graph->nodes.find(from)==graph->nodes.end())
{
graph->nodes[from]=new Node(from);
}
if(graph->nodes.find(to)==graph->nodes.end())
{
graph->nodes[to]=new Node(to);
}
Node* fromnode=graph->nodes[from];
Node* tonode=graph->nodes[to];
Edge* newedge=new Edge(weight,fromnode,tonode);
fromnode->nexts.push_back(tonode);
fromnode->out++;
fromnode->edges.push_back(newedge);
tonode->in++;
graph->edges.insert(newedge);
}
return graph;
}
可以清楚的看出每个结点的入度,出度,指向的结点,散发出的边。
图的广度(宽度)优先遍历(用队列辅助)
步骤:1 利用队列实现
2. 从源节点开始依次按照宽度进队列,然后弹出
3.每弹出一个点,把该点邻接点中没有进过队列的放队列中
4.直到队列边空。
//宽度(广度)优先遍历
void bfs(Node* node)
{
if(node==NULL)
{
return;
}
deque<Node*> deq;
set<Node*> set;
deq.push_back(node);
set.insert(node);
while(deq.size()!=0)
{
Node* cur=deq.front(); //保存deq队列首元
deq.pop_front();//出队列打印
cout<<cur->value<<" ";
for(Node* next:cur->nexts)
{
if(set.find(next)==set.end())
{
deq.push_back(next);
set.insert(next);
}
}
}
cout<<endl;
}
图的广度优先遍历(用栈(本段代码用的deque,实际只尾端进出))
1.利用栈实现
2.从源节点开始把节点按照深度放入栈,然后弹出
3.每弹出一个点,把该结点下一个没有进过栈的邻接点放入栈
4.直到栈变空
//图的深度优先遍历
void dfs(Node* node)
{
if(node==NULL)
{
return;
}
deque<Node*> deq;
set<Node*> set;
deq.push_back(node);
set.insert(node);
cout<<node->value<<" ";
while(deq.size()!=0)
{
Node* cur=deq.back();
deq.pop_back();
for(Node* next:cur->nexts)
{
if(set.find(next)==set.end())
{
deq.push_back(cur);
deq.push_back(next);
set.insert(next);
cout<<next->value<<" ";
break;
}
}
}
}
谢谢阅读,欢迎指出错误!!