图论总结

本文是对图论的总结,重点介绍了最短路径算法Floyd、Dijkstra、Bellman-Ford和SPFA,以及最小生成树的Prim和Kruskal算法。此外,还提及了建图方法如边表和邻接矩阵,以及并查集的基本概念。

图论总结

图论定义:图G=(V,E)是一个二元组(V,E)使得E⊆[V]的平方,所以E的元素是V的2-元子集。为了避免符号上的混淆,我们总是默认V∩B=Ø。集合V中的元素称为图G的定点(或节点、点),而集合E的元素称为边(或线)。

外貌:

在这里插入图片描述
( 有点丑别建议哈)

图论特性:没有模板不能活,老死不相往来。

建图方法:边表,邻接矩阵

边表:一般用结构体存储

struct note
{
    int y,next,v;//y节点,next下一个,v之间的权值。
}a[E+8]//E为边数

邻接矩阵:用二维数组

bool f[][];//好用极了,但耗空间。表示i与j是否相连。

图论用法:

最短路:共有四种算法:Floyd,Dijkstra,Bellman_Ford,SPFA;

Floyd:是最短四算法中最简单的,但效率不咋地。
主体思路:i点如果和j点相连且j点和k点相连,那么i点和k点相连
优点:容易敲,容易理解,可以处理负权。
缺点:效率低O(n^3),不能处理负环。
void floyd(){
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    for(int k=1;k<=n;k++)
    if(d[i][k]+d[k][j]<d[i][j]){
        d[i][j]=d[i][k]+d[k][j];//只能用邻接矩阵
        path[i][j]=k;
    }
}
Dijkstra:是最短四算法中效率最高的(Orz,这是谁想出的,辣膜流)
主体思想:使用了广度优先搜索策略,以起始点为中心向外层层扩展,直到扩展到终点为止。
优点:因为遍历是节点多,效率较高O(n^2)
缺点:不能处理负权,且代码复杂。
void dijkstra(int r){
    for(int i=1;i<=n;i++)dis[i]=a[r][i];//还是用邻接矩阵,但也可以用邻接表
    memset(vis,false,sizeof(vis));//vis用于标记
    vis[r]=true;dis[r]=0;//起点赋值
    for(int i=1;i<n;i++)//只要循环n-1次
    {
        int minn=0x3f3f3f3f;
        int k=0;
        for(int j=1;j<=n;j++)
        if((!vis[j])&&(minn>dis[j])){
        minn=dis[j];
        k=j;
        }
    }
    if(k==0)return;
    vis[k]=k;
    for(int j=1;j<=n;j++)
    if((!vis[j])&&(dis[k]+a[c][j]<dis[j]))
    dis[j]=dis[k]+a[k][j];
}

当然,邻接表会更好:

void dijkstra()
{
    memset(dis,0x3f3f3f,sizeof(dis));//初始化
    vis[1]=1;
    dis[1]=0;
    for(int i=1;i<=n;++i)
    {
        int k=0;
        for(int j=1;j<=n;++j)//找出距离最近的点
            if(!vis[j]&&(k==0||dis[j]<dis[k]))
                k=j;
        v[k]=1;//加入集合
        for(int j=1;j<=n;++j)//松弛
            if(!v[j]&&dis[k]+a[k][j]<dis[j])
                dis[j]=dis[k]+a[k][j];
    }
}
Bellman_Ford:是最短四算法中唯一的只能判断算法,但是效率不高。
主体思想:对边进行松弛,直到无法松弛为止,若松弛次数超过n次,就有负环存在
优点:可以判断负权和负环
缺点:效率不高O(|n|*|E|),并难以做其他操作//E为边
bool Bellman_Ford()//只能判断
{
    for(int i=1;i<n;++i)
    for(int j=1;j<=e;++j)
    relax(edge[j].u,edge[j].v,edge[j].weight);
    bool flag=1;
    for(int i=1;i<=e;++i)
        if(dist[edge[i].v]>dist[edge[i].u]+edge[i].weight)//邻接表!!!
        {
            flag = 0;
            break;
        }
    return flag;
}
SPFA:Bellman_Ford的优化,用队列维护。
主体思想:在Bellman_Ford的基础下,用队列优化,把枚举值存入队列,队列可以用数组和queue。
优点:频频诈尸,初中阶段称王称霸。
缺点:死了,有神仙数据卡他。
void spfa(int s){
	memset(dis,INF,sizeof(dis)); //初始化每点i到s的距离
	dis[s]=0;vis[s]=true;q[1]=s; // 队列初始化,s为起点
	int v,head=0,tail=1;//队头队尾
	while(head<tail){ //  队列非空
		head++; 
		v=q[head]; // 取队首元素
		vis[v]=0;  // 释放队首结点,因为这节点可能下次用来松弛其它节点,重新入队
		for(int i=0; i<=n; i++) // 对所有顶点
		   if ((a[v][i]>0)and(dis[i]>dis[v]+a[v][i])){  
				dis[i]=dis[v]+a[v][i];  // 修改最短路
				if (!vis[i]){ // 如果扩展结点i不在队列中,入队
					q[++tail]=i;
					vis[i]=true;
				}
		   }
		
	}
}

最小生成树:共两种算法:Prim,Kruskal。

Prim:
主体思路:将图中的所有顶点分为两类集合:当前在最小生成树中的点集和当前不在最小生成树中的点集,开始时随机抽取一个点,作为起始点,放入最小生成树点集中。然后寻找连接点集中的点和不在点集中的点之间的边的最小值。将这个边放入最小生成树中,把对应连接的顶点放入已在最小生成树中的点集。
void Prim()
{
        while(1){//
        counter++;	//记录边的顺序编号
        int minn=56666,pos,start,flag=1;
        for(int j = 1;j<=n;j++) {	//查找所有连接点集中和非点集中的边的最小边
            if(markVertex[j]!=0) {
                for (int i=1;i<=n;i++) {
                    if (markVertex[i]!=0)continue;
                    if (list[j][i]!=-1 and list[j][i]<minn) {
                        flag=0;
                        minn=list[j][i];
                        pos=i;
                        start=j;
                    }
                }
            }
        }
        if(flag)break; //如果所有点都在最小生成树中,跳出循环
        markEdge[start][pos]=counter; //否则将边导入最小生成树
        markVertex[pos]=1;	//还有导入对应的集合外的点
    }
}//(秀了一波英语,no建议哈)
Kruskal:原理和prim算法差不多,然而kruskal并不是从源点开始层次考察的,而是直接用优先队列存储所有边,通过贪心算法的思想,用权重最小的边组成最小生成树。
并查集三工具准备
int Kruskal(int n,int m)
{
   int nEdge = 0, res = 0;
   qsort(a, n, sizeof(a[0]), cmp);
   for(int i = 0; i < n and nEdge != m - 1; i++){
       //判断当前这条边的两个端点是否属于同一棵树
       if(find(a[i].a) != find(a[i].b)){
           unite(a[i].a, a[i].b);
           res += a[i].price;
           nEdge++;
       }
   }
   //如果加入边的数量小于m - 1,则表明该无向图不连通,等价于不存在最小生成树
   if(nEdge < m-1) res = -1;
   return res;
}
小插曲:并查集入门:
只需要知道3个工具:G,M,J
int G(int x)
{
    if(father[x]==x)return x;
    father[x]=G(father[x]);
    return father[x]
}
void M(int x,int y)
{
    int fx,fy;
    fx=G(x);
    fy=G(y);
    father[fx]=fy;
}
bool J(int x,int y)
{
    int fx,fy;
    fx=G(x);
    fy=G(y);
    return (fx==fy);
}
完成。

———————————————————————————————————————————————

谢谢观看。

万水千山总是情,点赞一下行不行?

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

### 图论算法总结与例题 图论是一门研究图形结构及其性质的学科,在计算机科学中有广泛的应用。以下是几种常见的图论算法以及它们的相关例题。 #### 1. **广度优先搜索 (BFS)** 和 **深度优先搜索 (DFS)** 这两种基本的遍历方法可以用来解决许多基础问题,比如连通分量检测、路径寻找等。 - BFS通常适用于找到两点间的最短路径(无权图),其核心思想是以层次的方式扩展节点[^2]。 - DFS则常用于探索所有可能的状态或者回溯解决问题。 #### 2. **并查集** 并查集是一种处理动态集合的数据结构,主要用于快速判断两个元素是否属于同一个集合,并支持高效的合并操作。 - 并查集的主要功能包括查询和合并两部分。 #### 3. **最小生成树 (MST)** 最小生成树是指在一个加权连通图中找出一棵总权重最小的生成树。 - Prim算法通过不断选择离当前已构建的部分最近的顶点来逐步扩大这棵树[^1]。 - Kruscal算法则是按照边的权重从小到大依次考虑每条边,只要该边不会形成环就将其加入结果集中。 #### 4. **拓扑排序** 对于有向无环图(DAG),可以通过拓扑排序得到一个线性的顺序使得所有的前驱关系都满足。 - 方法通常是先统计入度再利用队列实现零入度节点移除的过程直到完成整个序列排列。 #### 5. **最短路径算法** 这些算法旨在计算从某个起点到达其他各点所需的最少成本。 - Dijkstra算法适合于非负权值的情况,它采用贪心策略每次挑选距离源点最近尚未访问过的节点作为新的候选者继续扩散影响范围[^3]。 - Bellman-Ford允许存在负权但需额外注意可能出现的负圈情况;SPFA是对前者的一种改进形式特别针对稀疏图表现更优[^4]。 - Floyd-Warshall能够一次性得出任意两点间最佳路线长度非常适合小型密集网络分析需求。 #### 实际应用举例 假设我们要设计一款游戏地图编辑器需要自动规划NPC巡逻线路,则可选用A*启发式寻径技术结合实际地形数据综合考量效率与合理性给出解决方案。 ```python import heapq def dijkstra(graph, start): distances = {node: float('infinity') for node in graph} distances[start] = 0 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances ``` 以上代码片段展示了如何使用Python实现Dijkstra算法以求解给定图上某一点至其余各点之间的最短路径。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值