一、图的表示
两种形式:
[1]邻接表:
struct Edge{
int from,to,cap,flow;//以最大流问题的数据结构为例
//from起点u;to终点v;
}
vector<Edge>edges;//保存每条边的信息并编号
int n,m;//n记录点数V,m记录边数量E
vector<int> G[MAXN];//G[u][v]存放从u出发到v的边在edges向量中的下标;
//G[u]存放了以u为起点的所有边在edges向量中的下标;
void add_edge(int from,int to,int cap){//添加边操作
edges.push_back((Edge){from,to,cap,0});//边
edges.push_back((Edge){to,from,0,0});//残量网络反向边
int m = edges.size();
G[from].push_back(m-2);//G[from]包含以from为起点的所有边
G[to].push_back(m-1);//记录对应边在edges中的下标
}
邻接表表示由一个包含V条链表的数组Adj组成,每个节点有一条链表,对于每个节点u∈V,有邻接表Adj[u]所包含所有与节点u邻接的节点。
我的邻接表没有使用指针,因为目前我在图算法的学习中暂时没有碰到需要删除边的操作,所以就先使用vector来代替链表,这样写起来也更方便一点。
[2]邻接矩阵
//数组形式
int a[MAXN][MAXN];
void add_edge(int from,int to,int dis){
a[i][j]=min(a[i][j],dis);//这里用最短路为例,如果存在重边则取最短的
}
//vector向量形式
vector< vector<int> > a(MAXN,vector<int>(MAXN,0));//二维vector的使用
void add_edge(int u,int v,int dis){
a[u][v]=dis;
}
以Adj[1]为例,链表头节点后面又2和5两个,表示e(1,2)和e(1,5)两条边。
算法导论中说可以用邻接表存放边的权重,如最短路中的distance和最大流中的cap。我代码里通过在结构体里面多增加了一个元素。
课后练习:
1-1:
Q:给定邻接链表,需要多长时间才能计算出每个节点的出度和入度?
A:出度的话如果是vector实现的化,直接调用size()可以O(1),链表的话应该是O(E);入度的话如果在最大流问题中,因为储存了残量网络中的反向边,所以也可以O(1),其他情况应该需要给O(e(V*E);
//后面的感觉有点难以后再写吧2333
二、广度优先算法BFS
[1]算法思想
Prim最小生成树算法和Dijkstra单源最短路径算法都使用了类似广度优先搜索的思想。
给定图G=(V,E)和一个可以识别的源节点s,广度优先可以发现从源节点s到达的所有节点。
在广度优先搜索树里从节点s到v的简单路径所对应的就是G中节点s到v的经过边数最少的路径。这个算法适用于有向图和无向图。
广度优先搜索,也就是再发现所有距离节点s为k的所有点之后,才会发现离源点距离为k+1的点。这里的距离也是指经过的边数,如果是最短路问题中的距离其实是边权值之和。
我是用结构体和stl队列容器实现的bfs;
struct Node{//结构体根据需求来,这里以二维的坐标节点为例
int x,y,steps;//x,y和步数
}
queue<Node> q;
void bfs(int startx,int starty){
q.push(Node(startx,starty,0));//推入第一个起点进队列
while(!q.empty()){
Node now;
now=q.front();//先进先出,取出优先级最高的即离起点距离最短的
q.pop();
if(now.x==desx&&now.y==desy){//如果到达目标节点
输出语句
break;
}
for(int i=0;i<4;i++){
if(OK(now.x+dir[i][0],now.y+dir[i][1]){//如果到这个点是可以有路的
q.push(Node(now.x+dir[i][0],now.y+dir[i][1],now.steps+1));
//把这个点推入队列。
}
}
}
}