目录
- 图的储存(邻接表和邻接矩阵)
- 图的遍历(DFS和BFS)
- 最短路径(Dijkstra算法、Bellman-Ford算法与SPFA算法,Floyd算法)
- 最小生成树(Prim算法,Kruskal算法)
- 拓扑排序
- 关键路径
1. 图的储存
1.1 邻接矩阵
就是一个二维数组G[N][N],当G[i][j]=1时,说明顶点i和顶点j存在边;当当G[i][j]=0时,说明顶点i和顶点j不存在边。
1.2 邻接表
把一个顶点的所有出边都放在一个列表中那么N个顶点就有N个列表,这N个列表称为图G的邻接表,称为Adj[N]。可用链表实现,但用变长数组实现vector<int> Adj[N];
比较简单且不容易出错。若同时存放编号和边权,可以用Node结构体
struct Node{
int v; //边的终点编号
int w;//边的边权
}
之后,边长数组就可以这样vector<Node> Adj[N]
声明。
如果要添加边,可以这样
Node temp;
temp.v=2;
temp.w=4;
Adj[1].push_back(temp);
更快的方法是构建Node结构函数
struct Node{
int v,w;
Node(int _v,int_w) : v(_v),w(_w) {}
}
如果要添加边,可以这样
Adj[1].push_back(Node(2,4));
2.图的遍历
2.1 DFS
2.1.1 方法
沿着一条路径直到无法继续前进,才退回到路径上离当前顶点最近的还存在未访问分支顶点的岔路口,并前往访问那些未访问分支顶点,直到遍历完整个图。(对比树)
2.1.2 伪代码
DFS(u){
//访问顶点u
vis[u]=true;
for(u能到的结点v){
if(vis[v]==false){
DFS(v); //递归访问v
}
}
}
DFSTrave(G){
//遍历图G
for(G的所有顶点u){
if(vis[u]==false){
DFS(u); //访问u所在的连通块
}
}
}
2.1.3 实现
邻接矩阵版
const int maxv=100;
const int inf=10000000;
int n,G[maxv][maxv];
bool vis[maxv]={
false};
void DFS(int u,int depth){
vis[u]=true;
//如果对u有什么操作,可以写在这里,如输出等
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
DFS(v,depth+1);
}
}
}
void DFSTrave(){
for(int u=0;u<n;u++){
if(vis[u]==false){
DFS(u,1);
}
}
}
邻接表
const int maxv=100;
const int inf=10000000;
int n;
bool vis[maxv]={
false};
vector<int> Adj[maxv];
void DFS(int u,int depth){
vis[u]=true;
for(int i=0;i<Adj[u].size();i++){
//对从u出发可以到达的所有顶点v
int v=Adj[u][i];
if(vis[v]==false){
DFS(v,depth+1);
}
}
}
void DFSTrave(){
for(int u=0;u<n;u++){
if(vis[u]==false){
DFS(u,1);
}
}
}
2.2 BFS
2.2.1 方法
BFS一般使用队列,通过反复取出队首结点,将该顶点可到达的未曾加入过队列的顶点全部入队。(对比树)
2.2.2 伪代码
BFS(u){
queue q;
将q入队;
inq[u]=true; //设置u是否进入过队列
while(q非空){
取出q的队首元素u进行访问;
for(u可到的所有结点v){
if(inq[v]=false){
将v入队;
inq[v]=true;
}
}
}
}
BFSTrave(G){
for(G的所有顶点u){
if(inq[u]==false){
BFS(u);
}
}
}
2.2.3 实现
邻接矩阵
int n,G[maxv][maxv];
bool inq[maxv]={
false};
void BFS(int u){
queue<int> q;
q.push(u);
inq[u]=true;
while(!q.empty()){
int u=q.front();
q.pop();
for(int v=0;v<n;v++){
if(inq[v]==false&&G[u][v]!=inf){
q.push(v);
inq[v]=true;
}
}
}
}
BFSTrave(G){
for(int u=0;u<n;u++){
if(inq[u]==false){
BFS(q);//遍历u所在的连通块
}
}
}
邻接表
小应用:给定顶点,输出该连通块内所有顶点的层号(与这些差不多的题目只需稍微修改一下模板就可以了)
struct Node{
//也可以用Node函数的方式
int v; //顶点编号
int layer; //层号
};
vector<Node> Adj[N];
void BFS(int s){
//起始顶点编号
queue<Node> q;
Node start; //其实顶点编号
start.v=s;
start.layer=0;
q.push(start);
inq[start.v]=true;
while(!q.empty()){
Node top=q.front();
q.pop();
int u=top.v; //队首节点的编号
for(int i=0;i<Adj[u].size();i++){
Node next=Adj[u][i];
next.layer=top.layer+1;
if(inq[next.v]==false){
q.push(next);
inq[next.v]==true;
}
}
}
}
3.最短路径
3.1 Dijkstra 算法(单源最短路径)
3.1.1 方法
设置集合S存放已被访问的结点,然后执行n次以下步骤
1.每次从集合V-S(未被访问)中选择与起点s的最短距离最小的一个顶点(记为u),标记成访问(加入集合S)。
2. 之后,以顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。
3.1.2 伪代码(只是求出最短路径)
//G为图,一般设置成全局变量,数组d为源点到达各点的最短路径长度,s为起点
Dijkstra(G,d[],s){
初始化;
for(循环n次){
u=使d[u]最小的还未访问的顶点的编号;
标记u被访问;
for(u能到的顶点v){
if(v没有被访问&&以u为中介点使s到v的最短路径d[v]更优){
优化d[v]; //又叫松弛操作
}
}
}
}
3.1.3 实现(求出最短路径+解最短路径)
3.1.3.1 最普通的模板
const int maxv=1000;
const int inf=100000000000;
int n,G[maxv][maxv];
int d[maxv]; //记录到达各点的最短路径长度
int pre[maxv]; //记录前驱顶点
bool vis[maxv]={
false};
void Dijkstra(int s){
//初始化
fill(d,d+maxv,inf);
d[s]=0;
for(int i=0;i<n;i++){
//u=使d[u]最小的还未访问的顶点的编号;
int u=-1,MIN=inf;
for(int j=0;j<n;j++){
if(vis