图算法知识点和模板(未完待续)

本文详细介绍了图的存储方式(邻接矩阵与邻接表),并深入探讨了图的遍历(DFS和BFS),接着讲解了最短路径算法,包括Dijkstra、Bellman-Ford和SPFA算法,以及Floyd算法。此外,还涵盖了最小生成树的Prim和Kruskal算法,拓扑排序的定义和实现,以及关键路径的相关概念。
摘要由CSDN通过智能技术生成

目录

  1. 图的储存(邻接表和邻接矩阵)
  2. 图的遍历(DFS和BFS)
  3. 最短路径(Dijkstra算法、Bellman-Ford算法与SPFA算法,Floyd算法)
  4. 最小生成树(Prim算法,Kruskal算法)
  5. 拓扑排序
  6. 关键路径

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));

链表实现
vector数组实现

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
/* * (有向)的深度优先遍历算法模板 */ package dsa; public abstract class DFS extends GraphTraverse { //变量 protected static int clock = 0;//遍历过程中使用的计时钟 //构造方法 public DFS(Graph g) { super(g); } //深度优先遍历算法 protected Object traverse(Vertex v, Object info) {//从顶点v出发,做深度优先查找 if (UNDISCOVERED != v.getStatus()) return null;//跳过已访问过的顶点(针对非连通) v.setDStamp(clock++); v.setStatus(DISCOVERED); visit(v, info);//访问当前顶点 for (Iterator it = v.outEdges(); it.hasNext();) {//检查与顶点v Edge e = (Edge)it.getNext();//通过边e = (v, u) Vertex u = (Vertex)e.getVPosInV(1).getElem();//相联的每一顶点u switch (u.getStatus()) {//根据u当前的不同状态,分别做相应处理 case UNDISCOVERED ://若u尚未被发现,则 e.setType(TREE);//e被归类为“树边” traverse(u, info);//从u出发,继续做深度优先查找 break; case DISCOVERED ://若u已经被发现,但对其访问尚未结束,则 e.setType(BACKWARD);//将e归类为“后向跨边” break; default ://VISITED,即对u的访问已经结束 if (u.getDStamp() < v.getDStamp())//若相对于v,u被发现得更早,则 e.setType(CROSS);//将e归类为“横跨边” else//否则 e.setType(FORWARD);//将e归类为“前向跨边” break; } }//至此,v的所有邻居都已访问结束,故 v.setFStamp(clock++); v.setStatus(VISITED);//将v标记为VISITED return null;//然后回溯 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值