19.图和树基础

一、基本介绍

1.图

描述的是一些个体之间的关系。这些个体之间既不是前驱后继的顺序关系,也不是祖先后代的层次关系,而是错综复杂的网状关系。我们一般用 G = ( V , E ) G=(V,E) G=(V,E) 来表示, V V V 表示结点, E E E 表示边。

  • 根据边是否有权值,分为带权图和不带权图,也可将不带权图视为权重都为 1 1 1 的图。

  • 根据边是否有方向,分为有向图和无向图。

  • 根据稠密程度(边的条数 ∣ E ∣ |E| E ∣ V ∣ 2 |V|^2 V2)的关系,分为稠密图和稀疏图。

下面给出了一些简单的图的例子:

image-20230128140628313image-20230128140642453

2.树

是一种特殊的图,它满足 ∣ V ∣ = ∣ E ∣ + 1 |V|=|E|+1 V=E+1。将其中一个结点作为根节点,表示所有结点的祖先

注意,对于一些树来说,尤其是无向图,在题目未说明的情况下,根节点是任意的,取决于你如何选择。通常题目会以编号为 1 1 1 的节点作为根节点,但有些进阶的题目会要求你选择恰当的根节点来降低算法的复杂度。

对于树上的连边,靠近根节点的结点被叫做父结点,远离根节点的结点被叫做子节点。一个结点只有一个父节点,但可以有多个子节点。具有相同父节点的结点互称为兄弟结点

image-20230128140659218

二、一些性质

1.度

对于无向图来说,一个结点的度等于该结点所连的边数。

对于有向图来说,一个结点的入度等于连向该结点的边数,一个结点的出度等于连出该结点的边数。

2.子图

对于图 G = ( V , E ) G=(V,E) G=(V,E) 和图 G ′ = ( V ′ , E ′ ) G'=(V',E') G=(V,E) 来说,如果满足 V ′ ∈ V , E ′ ∈ E V'\in V,E'\in E VV,EE,则 G ′ G' G G G G 的子图。

3.连通性

  • 对于无向图 G = ( V , E ) G=(V,E) G=(V,E) 来说:

    • 如果任意两个结点之间,都有一条通路,那么该图是一个连通图
    • 无向边构成的树一定是一个连通图,且任意两点之间仅存在一条简单路径(无重边)。
    • 如果一个子图 G ′ G' G 是一个连通图,则称 G ′ G' G连通子图
  • 对于有向图 G = ( V , E ) G=(V,E) G=(V,E) 来说:

    • 如果任意两个结点之间,都有一条通路,那么该图是一个强连通图
    • 如果一个子图 G ′ G' G 是一个连通图,则称 G ′ G' G 为强连通子图(强连通分量)。

三、图的表示

1.邻接矩阵

邻接矩阵即使用一个二维数组来完全表示一个图。

  • 稠密图的情况下,我们更多使用邻接矩阵来表示图。
  • 如果两个点之间存在多条边,那么可能邻接矩阵将并不适用。
  • 邻接矩阵在稀疏图的情况下,容易受点数限制。

m p [ u ] [ v ] = w mp[u][v]=w mp[u][v]=w 表示存在一条从结点 u u u 到结点 v v v 的权重为 w w w 的边。

对于无向边,可以视为两条方向相反、连接结点相同的边。

const ll maxn=1010;
ll n,m,mp[maxn][maxn];
int main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=m;i++)
	{
		ll u,v,w;
		scanf("%lld%lld%lld",&u,&v,&w);
		mp[u][v]=w;
        //mp[v][u]=w;
	}
	
	return 0;
}

2.邻接表(链式前向星)

邻接表在表示稀疏图时非常紧凑,节省空间,所以成为通常用来表示图的方法。

数组 p [ u ] p[u] p[u] 表示结点 u u u 的最后一条边, p [ u ] = = − 1 p[u]==-1 p[u]==1 则表示点 u u u 没有别的边了。 t t t 表示边的数量。

结构体数组 e [ t ] e[t] e[t] 表示第 t t t 条边的所有信息,其中 v v v 表示边的终点, w w w 表示边的权重, n e x t next next 表示具有同样起点的另一条边。

const ll maxn=100010;
const ll maxm=100010;
struct node
{
	ll v;
	ll w;
	ll next;
}e[maxm*2];
ll n,m,p[maxn],t=0;
void insert(ll u,ll v,ll w)
{
	e[t].v=v;
	e[t].w=w;
	e[t].next=p[u];
	p[u]=t++;
}
int main()
{
	memset(p,-1,sizeof(p));
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=m;i++)
	{
		ll u,v,w;
		scanf("%lld%lld%lld",&u,&v,&w);
		insert(u,v,w);
        //insert(v,u,w);
	}
	
	return 0;
}

四、图的遍历

1.邻接矩阵

void dfs(ll u,ll pre)
{
    cout<<"u="<<u<<endl;
    for(ll i=1;i<=n;i++)
        if(i!=pre && mp[u][i]!=0)
            dfs(i,u);
}

2.邻接表

(1)DFS

void dfs(ll u,ll pre)
{
    cout<<"u="<<u<<endl;
    for(ll i=p[u];i!=-1;i=e[i].next)
    {
        ll v=e[i].v;
        if(v!=pre)
            dfs(v,u);
    }
}

(2)BFS

void bfs(ll start)
{
    queue<ll> q;
    cout<<"start="<<start<<endl;
    vis[start]=true;
    q.push(start);
    while(!q.empty())
    {
        ll u=q.front();
        q.pop();
        for(ll i=p[u];i!=-1;i=e[i].next)
        {
            ll v=e[i].v;
            if(!vis[v])
            {
                vis[v]=true;
                q.push(v);
            }
        }
    }
}

五、作业

1.橙题

B3862 图的遍历(简单版)

B3613 图的存储与出边的排序

B3643 图的存储

P5318 【深基18.例3】查找文献

2.黄题

P3916 图的遍历

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值