图的定义、建立、搜索

疯狂咕咕咕咕咕咕咕咕。—2019.1.21(晚)

考完试了,拖一拖,看一看,慢慢学(懒)。

为了能更加准确地表达表达概念,我将会从《挑战程序设计竞赛》中摘取部分内容用于博客描述。此外,以下所有图片均引用自《挑战程序设计竞赛》。

什么是图

字好多有不想总结,感觉很容易看懂但又不能缺,于是……(抱歉截图了)(图片皆引用自《挑战程序设计竞赛》)

概念、定义

引用自《挑战程序设计竞赛》
引用自《挑战程序设计竞赛》
引用自《挑战程序设计竞赛》

引用自《挑战程序设计竞赛》

自我总结

前几章所讲的堆、树其实都是图的一种,它们所表达的仅仅是顶点与顶点之间的关系,边的作用只是表示联系。而在图中,边拥有了自己的数值量,可以被赋予各种属性,如权值。权值可以代表时间、距离以及价格等多种不同的属性。
再附上百度百科里对的定义(二元组):图G是一个有序二元组(V,E),其中V称为顶集(Vertices Set),E称为边集(Edges set),E与V不相交。它们亦可写成V(G)和E(G)。E的元素都是二元组,用(x,y)表示,其中x,y∈V。总结一下:想要用计算机记录一个图,要记录两套数据:点的值(V顶集),点之间的关系(E边集)。这也是下面在图的实现中所要学习的。写这最后一段的主要原因是之前看别人的博客全是用这种东西写证明过程的,菜鸡完全看不懂,就在这稍微提及一下。

图的实现

从图的定义可知,一个图的实现要记录它的顶点和顶点与顶点之间的关系。虽然在画的时候看似不规则,但实际的实现方式依旧是数组。

邻接矩阵

邻接矩阵是使用V× V的二维数组来表示图。g[i] [j]表示的是顶点i与顶点j之间的关系。
在无向图中,用1表示连接(表示边数,2可以表示有两条边,即重边,3及其他以此类推),用0表示断,且对称,输入时g[i][j]与g[j][i]等值;有向图则根据要求实现。
在这里插入图片描述
在这里插入图片描述

short mp[MAXN][MAXN];
int a,b;
memset(mp,0,sizeof(mp));
for(int i=0;i<E;i++)
{
    cin>>a>>b;//输入两点
    mp[a][b]=1;mp[b][a]=1;//标记,有向图只标记一个
}

在带权图中,g[i][j]存的是由顶点i到顶点j的边的权值。在边不存在的情况下,g[i][j]的值为INF(较大的常数,如0x7fffffff)
在这里插入图片描述

typedef 0x7fffffff INF;
short mp[MAXN][MAXN];
int a,b,c;
memset(mp,INF,sizeof(mp));
for(int i=0;i<E;i++)
{
    cin>>a>>b>>c;//输入点和权
    mp[a][b]=c;//记录
}

邻接矩阵的好处是可以在常数时间内判断两点间是否有边存在,但要花费V2的空间。在边很少的稀疏图里十分浪费,且在边数多的时候有爆空间的危险。此外,如果在带权图中有重边且都必须记录的话,邻接矩阵也无法实现,因此,就需要有第二种实现法—邻接表。

邻接表

邻接表是用动态数组加结构体来记录边。具体实现方法按习惯写,下面是例子:
在这里插入图片描述
实现代码:(不带权就不用写结构体了)

#include <iostream>
#include <vector>//动态数组
#define MAXN 0x7fffff
using namespace std;
struct edge{int to,cost;};//结构体存值
vector<edge> infor[MAXN];
int v,e,a,b,c;
int main()
{
    int i,j;
    cin>>v>>e;
    for(i=0;i<e;i++)
    {
         cin>>a>>b>>c;//a是起点,b是终点,c是权值
         edge t;
         t.to=b;t.cost=c; 
         infor[a].push_back(t); 
    }
    for(i=1;i<=v;i++)
        for(j=0;j<infor[i].size();j++)
            cout<<i<<" "<<infor[i][j].to<<" "<<infor[i][j].cost<<endl;
 return 0;
}

与邻接矩阵相比,邻接表占用更少的内存,但实现更为复杂,且询问两点间是否有边时需要遍历一遍才能知道。

其实也可以乱序直接结构体数组记录边,但那样无法便捷的对图进行搜索,具体的做图方式还是应该根据实际情况来做决定。

图的搜索

搜索的话主要是用dfs或bfs,在开个visit数组打标记即可。
邻接矩阵dfs主体:

void dfs(int a)
{ 
    for(int i=1;i<=v;i++)
        if(mp[a][i]!=INF&&!vis[i])//mp[a][i]!=0
        {
            vis[i]=true;
            *//
            *//
            *//添加内容
            dfs(i);
        }
{

邻接表dfs主体:

vector<int> infor[MAXN];
void dfs(int a)
{
    for(int i=0;i<infor[a].size();i++)
        if(!vis[infor[a][i]])
        {
            vis[infor[a][i]]=true;
            *//
            *//
            *//添加内容;
            dfs(infor[a][i]);
        }
}

要注意的是图不一定是连通图,所以要在外面开一个for循环保证遍历完全:(也可以作为是否连通或是否是树的判断)

for(int i=1;i<=v;i++if(!vis[i])
        dfs(i);

在图的搜索中,由于每个顶点和边都只访问了一次,所以复杂度为O(V+E)。
此外,也可以用dfs求图的拓扑序(不会不会先空着吧)。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值