图的算法及函数操作

本文深入探讨图算法的核心概念,涵盖邻接矩阵与邻接表表示,边的增删操作,路径检测,树检测,以及Prim、Kruskal、Warshall算法的原理与C语言实现,详述DFS与BFS算法在不同场景的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

表示图的不同方式

  1. 邻接矩阵(缺点:要在开始前说明顶点的个数)
  2. 邻接表

如何使用邻接矩阵建立模型中的图中加边

在边i和j之间添加一条边,设M(i,j) = 1,这里 M 是图 G 的邻接矩阵

int a[10][10] = {0};
void addedge(int start, int end)
{
if(a[start][end] == 0;
a[start][end] = 1;
}

删除边操作

void remove(int start, int end)
{
a[start][end] == 0;
}

检测两个节点之间是否存在一条路径

int path[max][max] = {0};
void getpath()
{
int cr = max;
int cc = max;
int i,j,k;
for(i = 0; i < max; i++)
for(j = 0; j < max; j++)
for(k = 0; k < max; k++)
path[i][j] += a[i][k] * a[k][j];
}

如何检测图是否是一棵树

int tree(int a[max][max])
{
int i , j;
for(i = 0; i < max ; i++)
for( j = 0; j < max; j++)
if(a[i][j] > 0 && a[j][i] > 0)
return 0;

return 1;

Prim 算法

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。

算法实现步骤

  1. 选择任意顶点
  2. 将所选顶点
  3. 重复步骤4,5,直到在所有顶点都在生成树中为止
  4. 找到一个具有最小边长但还没有在生成树中的顶点
  5. 把这个顶点添加到生成树中
  6. 所有顶点都在生成树中,停止算法
代码实现 C语言
#include<stdio.h>
#include<stdlib.h>
#define max 1000000000;
inta[1005][1005],d[1005],p[1005];
int main()
{
int i,j,k,m,n,min,ans,t;
int x,y,z;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[x][y]=z;
a[y][x]=z;
}
for(i=1;i<=n;i++)
d[i]=1000000000;
d[1]=0;
for(i=2;i<=n;i++)
{
min=max;
for(j=1;j<=n;j++)
if(!p[j]&&min>d[j])
min=d[j];
t=j;
}
p[t]=j;
for(j=1;j<=n;j++)
if(a[t][j]=0&&d[j]>a[t][j])
{
d[j]=a[t][j];
ans+=min;
}
printf("%d",ans);
return0;
}

Kruskal算法

Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪心算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。

问题一 对图的所有边按照权值大小进行排序。
问题二 将边添加到最小生成树中时,怎么样判断是否形成了回路。

问题一很好解决,采用排序算法进行排序即可。

问题二,处理方式是:记录顶点在"最小生成树"中的终点,顶点的终点是"在最小生成树中与它连通的最大顶点"(关于这一点,后面会通过图片给出说明)。然后每次需要将一条边添加到最小生存树时,判断该边的两个顶点的终点是否重合,重合的话则会构成回路。
算法实现步骤
1.新建图G,G中拥有原图中相同的节点,但没有边;
2.将原图中所有的边按权值从小到大排序;
3.从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中;
4.重复3,直至图G中所有的节点都在同一个连通分量中。

c语言实现
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int M = 1e5+7;
 
struct node
{
  int a,b,val;
} Q[M];
int fa[M];
 
int Rd()
{
  int res=0;char c;
  while(c=getchar(),!isdigit(c));
  do {
    res=(res<<3)+(res<<1)+(c^48);
  } while(c=getchar(),isdigit(c));
  return res;
}
 
bool cmp(LZ a,LZ b){
  return a.val<b.val;
}
 
int getfa(int v){
    if(fa[v]!=v)fa[v]=getfa(fa[v]);
    return fa[v];
}
 
int main()
{
    int i,j,n,m,x,y;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
    {
        Q[i].a=Rd();Q[i].b=Rd();Q[i].val=Rd();
    }
    sort(Q+1,Q+m+1,cmp);
    for(i=1;i<=n;i++)
    {
        fa[i]=i;
    }
 
    int sum=0,cut=0;
    for(i=1;i<=m;i++)
    {
        x=getfa(Q[i].a);
        y=getfa(Q[i].b);        
        if(x==y)continue;        
        sum+=Q[i].val;        
        if(++cut==n-1)break;        
        fa[x]=y;    
    }    
    printf("%d",sum);    
    return 0;
}

使用Warshall算法得到最短路

int minoftwo(int a,int b)
{
    if(a == b)
        return a;
    else
        return a>b?a:b;
}
void path(int adj[max][max])
{
    int i,j,k;
    int q[max][max] = {0};
    for(i = 0; i < max; i++)
    {
      for(j = 0 ; j < max; j++)
        {
          if(adj[i][j] == 0)
          q[i][j] = INF;
          else 
          q[i][j] = adj[i][j];
        }
    }
    for(k = 0;  k < max; k++)
    {
    for(i = 0; i < max; i++)
    {
    for(j = 0; j < max; j++)
    {
    q[i][j] = minoftwo(	q[i][j],q[i][k]+q[k][j]);
}
}
}
}

DFS 算法

实现靠栈

应用
1.找出图中所有连接部件
2.找出一个链接部件内所有结点
3.求解迷宫问题

举例
SDUT OJ DFS

BFS 算法

实现靠队列

应用
1.prim 的MST算法
2.Dijkstra 的单元最短路径算法
3.复制集合,Cheney算法
4.构建Web搜索代理
举例
SDUT OJ BFS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值