图论基础(1)

图论基础(1)

入门级文章

1.图基本介绍

  • 什么是图?

图是由顶点的非空有穷集合(用V表示该集合)与顶点之间的关系(边或弧)的集合(用E表示该集合)构成的结构。可以形式化表示为G=(V,E)

其中,V为顶点的非空有穷集合,E为关系的有穷集合。

根据顶点与顶点之间的关系分为有向图和无向图!!

说白了就是有很多顶点,点与点之间有联系

  • 名词术语

顶点的度:某个顶点V的度是指依附于该顶点的边或弧的数量,说白了就是跟这个点连的有几条线

出度、入度:对于有向图还要区分出度和入度,一个顶点V的出度是指以该顶点为出发点的弧(边)的数量;一个顶点V的入度是指以该顶点为终止点的弧(边)的数量

路径:对于顶点i和j,存在着i->a->b->c->…->j(从i出发能够到达j的顶点序列),如果i==j,则此路径成为回路或者环路。

子图:对于G=(V,E)与G’=(V’,E’),若有V’∈V,E’∈E, 则称G’是G的一个子图,一个图是其自身的子图,说白了就当做子集来理解吧

图的连通:(1)无向图的连通:若顶点i与j之间有路径,就称i与j是连通的,若图中任意两个顶点之间都有路径,就称该无向图是连通的。(2)有向图的连通:若顶点i与j之间有路径,并且顶点j与i之间也有路径,就称i与j是连通的,若图中任意两个顶点之间都有连通,就称该有向图是强连通的。

连通分量:连通分量:无向图中的极大连通子图。任何一个连通图的连通分量只有一个,是它本身

强连通分量:有向图中的极大连通子图。

在这里插入图片描述

在这里插入图片描述

2.图的存储

  • 邻接矩阵来存储

无向图:用一个二维数组来存储点与点之间的关系,如果i到j有直接联系,则令chart[i ] [j ] = 1, 如果边有权值w,则

chart[i] [j] = w;如果没有直接关系则chart[i] [j] = 0(任意一个特殊标记都行,-1等等);由于这是无向图需要反过来再

存一遍即chart[j] [i] = 值;

在这里插入图片描述

有向图:用一个二维数组来存储点与点之间的关系,如果i到j有直接联系,则令chart[i ] [j ] = 1, 如果边有权值w,则

chart[i] [j] = w;如果没有直接关系则chart[i] [j] = 0(任意一个特殊标记都行,-1等等);由于这是有向图,所以不需要反过来再存一遍。

在这里插入图片描述

  • 邻接表来存储

每个点为头结点(i),跟它有直接关系的就直接加在以i为头结点的链表后面

无向图:(需要i->j存一遍,j->i再存一遍)

在这里插入图片描述

我们做算法题目的时候为了方便肯定是不会去写链表的,用vector代替或者用链式前向星来存图

有向图:

在这里插入图片描述

如果有权值,那么结点就弄成一个结构体

typedef struct{
    int end, w;//end表示终点,w表示权值
}Node

3.题目实战

题目连接:https://www.luogu.com.cn/problem/P5318

在这里插入图片描述

输入输出样例
输入 #1
8 9
1 2
1 3
1 4
2 5
2 6
3 7
4 7
4 8
7 8

输出 #1
1 2 5 6 3 7 8 4 
1 2 3 4 5 6 7 8 
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

int n, m;
//标记是否被遍历过了
bool flag[100005];
vector<int> a[100005];//二维动态数组
//深搜
//num表示还有多少个点没遍历
//其实num这个参数删了也行的,完全不影响
void dfs( int now, int num )
{
	flag[now] = true;
	cout << now << ' ';
	cout << now << ' ';
	
	for ( int i = 0; i < a[now].size(); ++i ) {
		if ( !flag[a[now][i]] ) {
			dfs(a[now][i]);
		}
	}
}
//广搜
void bfs()
{
	queue<int> q;
	q.push(1);
	flag[1] = true;
	while (!q.empty()){
		int s = q.front();
		q.pop();
		cout << s << ' ';
		for ( int i = 0; i < a[s].size(); ++i ) {
			if ( !flag[a[s][i]] ) {
				q.push(a[s][i]);
				flag[a[s][i]] = true;
			}
		}
	}
}

int main()
{
	cin >> n >> m;
	for ( int i = 0; i < m; ++i ) {
		int s, e;
		cin >> s >> e;
		a[s].push_back(e);
        //如果是无向图,需要加这个
        //a[e].push_back(s);
	}
    //为什么要排序?
    //看题目中:如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。
    //排序就保证了每个链表顶点编号是升序的了
	for ( int i = 0; i <= n; ++i ) {
		sort(a[i].begin(), a[i].end());
	}
	dfs(1);
	
	cout << '\n';
	
	for ( int i = 0; i <= n; ++i ) {
		flag[i] = false;
	}
	
	bfs();
	return 0;
}

由于这个题目数据太大,用邻接矩阵是存不下的,我下面给出邻接矩阵的版本,下面代码是过不了本题的

#include <iostream>
#include <queue>
using namespace std;
const int N = 20000;//大概只能这么大了

int chart[N][N], n, m;
bool flag[N];

void dfs( int now );
void bfs();
//解释一下这个为什么不需要排序
//因为每次找跟当前点相关的顶点的时候
//都是for( int i = 1; i <= n; ++i )
//默认是按顺序来的
int main()
{
	cin >> n >> m;
	for ( int i = 1; i <= m; ++i ) {
		int s, e;
		cin >> s >> e;
		chart[s][e] = 1;
		//如果是无向图,需要加这个
		//chart[e][s] = 1;
	}
	dfs(1);
	for ( int i = 0; i <= n; ++i ) {
		flag[i] = false;
	}
	cout << endl;
	bfs();
	return 0;
}

void dfs( int now )
{
	flag[now] = true;
	cout << now << ' ';
	for ( int i = 1; i <= n; ++i ) {
		if ( !flag[i] && chart[now][i] ) {
			dfs(i);
		}
	}
}

void bfs()
{
	queue<int> q;
	q.push(1);
	flag[1] = true;
	while (!q.empty() ) {
		int temp = q.front();
		cout << temp << ' ';
		q.pop();
		for ( int i = 1; i <= n; ++i ) {
			if ( !flag[i] && chart[temp][i] ) {
				q.push(i);
				flag[i] = true;
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值