图论基础

基本概念

1.图也是一种数据结构:是某类具体事物以及这些事物之间的联系。

2.图:顶点(vertex)和(edge)组成。
顶点:具体事物。
边:具体事物之间的联系。

顶点的集合V,边的集合E,所以图记为G = (V,E)。

比如下图就是一个典型的图。。
图论基础_第1张图片


图的分类

0.带权图

定义:边上带有权值的图。(不同问题中,权值意义不同,可以是距离、时间、价格、颜值 ……)


1.无向图

定义:边没有指定方向的图

相邻:两个顶点之间如果有边连接,那么就视为两个顶点相邻。
路径:相邻顶点的序列。
:起点和终点重合的路径。
连通图:任意两点之间都有路径连接的图。
:顶点连接的边数叫做这个顶点的度。
:没有圈的连通图。
森林:没有圈的非连通图


2.有向图

定义:边具有指定方向的图(有向图中的边又称为弧,起点称为弧头,终点称为弧尾

有向路径:相邻顶点的序列。
有向环:一条至少含有一条边且起点和终点相同的有向路径。
有向无环图(DAG):没有环的有向图。

:一个顶点的入度与出度之和称为该顶点的度。
1)入度:以顶点为弧尾的边的数目称为该顶点的出度
2)出度:以顶点为弧头的边的数目称为该顶点的入度


图的存储方式

1.邻接矩阵

E[u][v] 表示的是顶点u与顶点v的关系。

如果顶点u和顶点v之间有边相连,

E[u][v] = 1;

否则

E[u][v] = 0;

如果是有权边,

E[u][v] = cost;

对于无向图

E[u][v] = E[v][u];

优劣性:

优点:可以在常数时间内判断两点之间是否有边存在。
缺点:表示稀疏图时,浪费大量内存空间。
2.邻接表

通过把“从顶点0出发有到顶点2,3,5的边”这样的信息保存在vector中来表示图

邻接表非常的常用,所以我会在例题一解释它的具体用法。(当然所有的例题我都会使用邻接表进行存图


图的遍历方式

1.深度优先搜索(Depth-First Search)

遍历类似于树的先根遍历,是树的先根遍历的推广。

定义:假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。


T1 有向图的 dfs
#include 
#include 
#include 
using namespace std;

const int MAXN = 205;
vector<int> E[MAXN];
// 定义邻接表(edge)E
bool vis[MAXN];
// 记录是否访问过
void Addedge(int u, int v) {
// 存储边的信息
	E[u].push_back(v); // 表示以u为起点的边中有一条边的终点是v
}

void dfs(int i) {
    printf("%d ", i); 
    vis[i] = true;
    sort(E[i].begin(), E[i].end());
    // 因为要求字典序所以需要进行排序
    for (int j = 0; j < E[i].size(); j++) {
    // 枚举边
        if (vis[E[i][j]] == false) {
        	dfs(E[i][j]); // 继续进行dfs
		}
    }
}

int main() {
	int m, n;	
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++) {
    	int u, v;
        scanf("%d %d", &u, &v);
        E[u].push_back(v);
    }
    for(int i = 1; i <= n; i++) // 有可能不是连通图哦
    	if(vis[i] == false) // i没被访问过就dfs一次
    		dfs(i);
    return 0;
}

无向图同理。。。


广度优先搜索(Breadth-First Search)

遍历类似于树的按层次遍历的过程。

定义:假设从图中某顶点v出发,在访问v之后依次访问v的各个未被访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远,依次访问和v有路径相通且路径长度为1,2,…的顶点。


T2 有向图的 bfs
#include 
#include 
#include 
#include 
using namespace std;

const int MAXN = 5005;
vector<int> E[MAXN]; 
// 邻接表
bool vis[MAXN]; 
// 记录是否被访问过
void Addedge(int u, int v) {
	// 存图
	E[u].push_back(v);
}

void BFS(int st) {
	queue<int> q;
	q.push(st);
	// 初始节点进队
	vis[st] = true;
	printf("%d ", st);
	while(!q.empty()) { // 如果队列里还有节点
		int t = q.front();
		// 拿出第一个
		q.pop();
    	sort(E[t].begin(), E[t].end()); 
    	// 因为要求字典序所以需要进行排序
		for(int i = 0; i < E[t].size(); i++) {
		// 枚举边
			if(vis[E[t][i]] == false) {
				vis[E[t][i]] = true;
				printf("%d ", E[t][i]);
				q.push(E[t][i]);
				// 终点进队
			}
		}
	}
}

int main() {
	int n, m;
	scanf ("%d %d", &n, &m);
	for(int i = 1; i <= m; i++) {
		int u, v;
		scanf ("%d %d", &u, &v);
		Addedge(u, v);
	}
	for(int i = 1; i <= n; i++) // 有可能不是连通图哦
		if(vis[i] == false) // i没被访问过就dfs一次
			BFS(i);
	return 0;
}

无向图同理。。。

突破极限,一旦放弃了就意味着结束,说不定身体里还隐藏着连自己都没有察觉到的力量,不要被所谓的极限所禁锢。

你可能感兴趣的:(图论)