【7】图的基本概念、存储、遍历、拓扑排序

本文详细介绍了有向图的基础概念,包括度数、路径、环和完全图的区别,以及图的存储方式如邻接矩阵、邻接表等及其适用场景。重点讲解了深度优先搜索和广度优先搜索,并深入探讨了拓扑排序的原理与C++实现,包括BFS和DFS两种方法。
摘要由CSDN通过智能技术生成
  1. 图的基本概念
    (1) 有向图、无向图
    (2) 度数(出度、入度)
    (3) 简单图:不存在顶点到其自身的边,且同一条边不重复出现
    (4) 路径、环、简单路径
    (5) 无向完全图:任意两个顶点之间都存在边,有n个顶点的无向完全图有 n × (n - 1) / 2条边
    (6) 有向完全图:任意两个顶点之间都存在方向护卫相反的两条弧,有n个顶点的无向完全图有 n × (n - 1) 条弧
    (7) 稀疏图&稠密图:有很少条边或弧的图称为稀疏图,反之称为稠密图,相对的概念。

  2. 图的存储及基本操作
    (1) 邻接矩阵:适用于稠密图,可存有向图、无向图。常用。空间复杂度:O(n^2)。无法存重边。
    (2) 邻接表:适用于稀疏图,可存有向图、无向图。常用。空间复杂度:O(n + m)。可存重边无法存重边。
    (3) 邻接多重表,适用于稀疏图,可存无向图。不常用。空间复杂度:O(n + m)
    (4) 十字链表,适用于稀疏图,可存有向图、无向图。不常用。空间复杂度:O(n + m)。
    (5) 三元组表,适用于稀疏图,可存有向图,无向图。常用于Bellman-Ford算法、Kruskal算法。空间复杂度:O(m)。可存重边。

  3. 图的遍历
    (1) 深度优先搜索。邻接表存储的时间复杂度:O(n + m)。邻接矩阵存储的时间复杂度:O(n^2)
    (2) 广度优先搜索。邻接表存储的时间复杂度:O(n + m)。邻接矩阵存储的时间复杂度:O(n^2)

  4. 拓扑排序

存在拓扑排序等价于不存在环

有向图的拓扑序列

题目描述

给定一个 n n n 个点 m m m 条边的有向图,点的编号是 1 1 1 n n n,图中可能存在重边和自环。

请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 − 1 -1 1

若一个由图中所有点构成的序列 A A A 满足:对于图中的每条边 ( x , y ) (x, y) (x,y) x x x A A A 中都出现在 y y y 之前,则称 A A A 是该图的一个拓扑序列。

输入格式

第一行包含两个整数 n n n m m m

接下来 m m m 行,每行包含两个整数 x x x y y y,表示存在一条从点 x x x 到点 y y y 的有向边 ( x , y ) (x, y) (x,y)

输出格式

共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。

否则输出 − 1 -1 1

数据范围

1 < = n , m < = 1 0 5 1 <= n,m <= 10^5 1<=n,m<=105

输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3

C++ 代码

bfs的写法

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, M = 100010;
int n, m;
bool st[N];
int d[N], q[N]; 
struct Node{
	int id;
	Node* next;
	Node(int _id): id(_id), next(NULL){}
}*head[N];
void add(int a, int b){
	auto p = new Node(b);
	p->next = head[a];
	head[a] = p;
}

bool topsort(){
	int hh = 0, tt = - 1;
	for(int i = 1; i <= n; i++)
		if(!d[i])
			q[ ++ tt] = i;
	while(hh <= tt){
		int t = q[hh ++];
		for(auto p = head[t]; p; p = p->next)
			if(-- d[p->id] == 0)
				q[++ tt] = p->id;
	}
	return tt == n - 1;
}
int main(){
	scanf("%d%d", &n, &m);
	while(m --){
		int a, b;
		scanf("%d%d", &a, &b);
		d[b] ++;
		add(a, b);
	}
	if(!topsort()) puts("-1");
	else{
		for(int i = 0 ; i < n; i++)
			printf("%d ", q[i]);
	}
	return 0;
}

dfs写法

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, M = 100010;
int n, m;
int st[N];
int q[N], top;
struct Node{
	int id;
	Node* next;
	Node(int _id): id(_id), next(NULL){}
}*head[N];
void add(int a, int b){
	auto p = new Node(b);
	p->next = head[a];
	head[a] = p;
}
bool dfs(int u){
	//0表示未搜索,1表示搜索中,2表示已经搜索完毕 
	st[u] = 1;
	for(auto p = head[u]; p; p = p->next){
		int j = p->id;
		if(!st[j]){
			if(!dfs(j)) return false;
		}
		else if(st[j] == 1) return false;
	}
	q[top ++] = u;
	st[u] = 2;
	return true;
}
bool topsort(){
	for(int i = 1; i <= n; i++)
		if(!st[i] && !dfs(i))
			return false;
	return true;
}
int main(){
	scanf("%d%d", &n, &m);
	while(m --){
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b);
	}
	if(!topsort()) puts("-1");
	else{
		for(int i = n - 1; i >= 0; i--)
			printf("%d ", q[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值