数据结构(3)——图

目录

声明

基础知识

图的表示

邻接矩阵

实现方式

邻接表

实现方式

不加权图

加权图

连通图的遍历

深度优先遍历

实现方式(无向图)

广度优先遍历

实现方式(无向图)

例题

例题:传递闭包

题面

解法 

代码


声明

由于本文章其中一些代码是本人很久以前写的,而有一些是本人现在写的,所以码风有些不同,请勿介意。这是本人肝了两个星期才完成的文章,希望你们能给我点赞收藏加关注。最好给我评论鼓励一下我哦,谢谢!

基础知识

图(graph)是一种网状数据结构,用于描述对象的集合以及对象之间的关系。其中,对象用顶点(vertex),也称为节点(node),简称点。对象之间的关系用连接顶点的边(edge)表示。若图中的每条边是单向的,则该图称为有向图;若图中的每条边是双向的,则该图称为无向图;若图中的每条边有权重,则该图称为加权图。研究图的数学分支被称为图论,这个我们以后会深入讲解。

图的表示

邻接矩阵

用一个二维数组标记两个节点是否相邻。G_{i,j} 表示节点 i 能到达节点 j 。若该图为加权图,则 G_{i,j} 标记节点 i 与节点 j 之间的边权,如果没有边的话通常设置其为无穷大或 -1

实现方式

int G[3500][3500];//声明部分
int n, m;//点数与边数
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; ++ i)
    {
        int u, v, w;
        cin >> u >> v;//不加权
        cin >> w;//加权
        G[u][v] = true;//有向图
        G[v][u] = true;//无向图
        G[u][v] = w;//有向加权图
        G[v][u] = w;//无向加权图
    }
    return 0;
}

邻接表

用邻接表更节省空间,因为邻接表使用的是 vector。其中 G_i 存储节点 i 能直接到达的节点。

实现方式

不加权图
vector<int> G[3500];//声明部分
int n, m;//点数与边数
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; ++ i)
    {
        int u, v, w;
        cin >> u >> v;//不加权
        cin >> w;//加权
        G[u].push_back(v)//有向图
        G[v].push_back(u)//无向图
    }
    return 0;
}
加权图
struct Node{
    int v, w;
};
vector<Node> G[3500];//声明部分
int n, m;//点数与边数
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; ++ i)
    {
        int u, v, w;
        cin >> u >> v;//不加权
        cin >> w;//加权
        G[u].push_back({v, w})//有向图
        G[v].push_back({u, w})//无向图
    }
    return 0;
}

其实还有一种叫链式前向星的表示方式,后面会讲到。

连通图的遍历

深度优先遍历

选择一个顶点为起点(通常为 1 号节点),并递归遍历其所有的邻接节点。为了防止一个节点被重复访问,我们需要一个 vis 数组来标记一个节点有没有被访问过。

实现方式(无向图)

void dfs(int u){
	cout<<u<<" ";
	vis[u]=true;
	for(auto v:G[u]){
		if(vis[v]==true){
			continue;
		}
		dfs(v);
	}
}

广度优先遍历

选择一个顶点为起点,再按照与起点的距离从小到大进行遍历,与广度优先搜索类似。

实现方式(无向图)

void bfs(int s){
	queue<pair<int,int>> q;
	vis[s]=true;
	q.push({s,0});
	while(!q.empty()){
		pair<int,int> t=q.front();
		q.pop();
		dis[t.first]=t.second;
		for(auto v:G[t.first]){
			if(vis[v]==true){
				continue;
			}
			vis[v]=true;
			cout << v << " ";
		}
	}
}

例题

例题:传递闭包

题面

解法 

这题数据可以使用邻接矩阵,所以我们就用邻接矩阵。我们可以从一个顶点开始深度或广度遍历,并将访问到的点标记为 1。我们可以枚举每个点 i。从点 i 开始深度或广度遍历,直到遍历完。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,vis[1100000],e[3500][3500];
void dfs(int u){
	vis[u]=true;
	for(int v=1;v<=n;v++){
		if(e[u][v]==false){
			continue;
		}
		if(vis[v]==true){
			continue;
		}
		dfs(v);
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>e[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		memset(vis,false,sizeof vis);
		dfs(i);
		for(int j=1;j<=n;j++){
			cout<<vis[j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

当然也可以用 Floyd 做,这个我们以后会讲到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值