1. 图的基本概念
(1) 有向图、无向图
(2) 度数(出度、入度)
(3) 简单图: 不存在顶点到其自身的边, 且同一条边不重复出现
(4) 路径、环、简单路径
(5) 无向完全图: 任意两个顶点之间都存在边, 有n个顶点的无向完全图有n×(n−1)‖2条边
(6) 有向完全图: 任意两个顶点之间都存在方向护卫相反的两条弧,有n个顶点的无向完全图有n×(n−1)条弧
(7) 稀疏图&稠密图: 有很少条边或弧的图称为稀疏图,反之称为稠密图, 相对的概念。
2. 图的存储及基本操作
(1) 邻接矩阵: 适用于稠密图,可存有向图、无向图。常用。空间复杂度:。C(n∧2)。无法存重边。
(2) 邻接表: 适用于稀疏图, 可存有向图、无向图。常用。空间复杂度:。o(n+m)。可存重边。
(3) 邻接多重表,适用于稀疏图, 可存无向图。不常用。空间复杂度:。o(n+m)。可存重边。
(4) 十字链表,适用于稀疏图,可存有向图、无向图。不常用。空间复杂度:(。O(n+m)。无法存重边
(5) 三元组表, 适用于稀疏图, 可存有向图, 无向图。常用于Bel1man−Eopd算法、Kruskal算法。空间复杂度: 0(m)。可存重边。
3. 图的遍历
(1) 深度优先搜索。邻接表存储的时间复杂度:。o(n+m)。邻接矩阵存储的时间复杂度: 0(n^2)
(2) 广度优先搜索。邻接表存储的时间复杂度:。o(n+m)。邻接矩阵存储的时间复杂度: 0(n^2)
4.拓扑排序
图的遍历
DFS
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
bool st[N];
struct Node
{
int val;
Node *next;
Node (int _val) :val(_val),next(NULL){}
};Node *head[N];
void add(int a,int b)
{
auto p=new Node(b);
p->next=head[a];
head[a]=p;
}
void bfs()
{
queue<int>q;
q.push(1);
st[1]=true;
while(q.size())
{
auto t=q.front();
q.pop();
cout<<t<<endl;
for(auto p=head[t];p;p=p->next)
{
auto j=p->val;
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
}
int main()
{
int n,m;
cin>>n>>m;
while (m -- )
{
int a,b;
cin>>a>>b;
add(a, b);
}
bfs();
}
BFS
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100010;
struct Node
{
int val;
Node *next;
Node (int _val) :val(_val) , next(NULL){ };
};
Node *head[N];
int r[N],q[N];
int n,m;
void add(int a,int b)
{
auto p=new Node(b);
p->next=head[a];
head[a]=p;
}
bool tro()
{
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{
if(r[i]==0)q[++tt]=i;
}
while(tt>=hh)
{
int t=q[hh++];
for(auto p=head[t];p;p=p->next)
{
if(--r[p->val]==0)
{
q[++tt]=p->val;
}
}
}
return n-1==tt;
}
int main()
{
cin>>n>>m;
while(m--)
{
int a,b;
cin>>a>>b;
r[b]++;
add(a,b);
}
if(!tro())
{
cout<<"-1"<<endl;
}
else
{
for(int i=0;i<n;i++)
{
cout<<q[i]<<" ";
}
}
}
给定一个 n个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
DFS
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100010;
struct Node
{
int val;
Node *next;
Node (int _val) :val(_val) , next(NULL){ };
};
bool st[N];
Node *head[N];
int r[N],q[N];
int n,m;
int top;
void add(int a,int b)
{
auto p=new Node(b);
p->next=head[a];
head[a]=p;
}
bool dfs(int u)
{
st[u]=1;
for(auto p=head[u];p;p=p->next)
{
auto t=p->val;
if(!st[t])
{
if(!dfs(t))return false;
}
else if(st[t]==1)return false;
}
q[top++]=u;
st[u]=2;
return true;
}
bool tro()
{
for(int i=1;i<=n;i++)
{
if(!st[i]&&!dfs(i))return false;
}
return true;
}
int main()
{
cin>>n>>m;
while(m--)
{
int a,b;
cin>>a>>b;
add(a,b);
}
if(!tro())
{
cout<<"-1"<<endl;
}
else
{
for(int i=n-1;i>=0;i--)
{
cout<<q[i]<<" ";
}
}
}
BFS
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100010;
struct Node
{
int val;
Node *next;
Node (int _val) :val(_val) , next(NULL){ };
};
Node *head[N];
int r[N],q[N];
int n,m;
void add(int a,int b)
{
auto p=new Node(b);
p->next=head[a];
head[a]=p;
}
bool tro()
{
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{
if(r[i]==0)q[++tt]=i;
}
while(tt>=hh)
{
int t=q[hh++];
for(auto p=head[t];p;p=p->next)
{
if(--r[p->val]==0)
{
q[++tt]=p->val;
}
}
}
return n-1==tt;
}
int main()
{
cin>>n>>m;
while(m--)
{
int a,b;
cin>>a>>b;
r[b]++;
add(a,b);
}
if(!tro())
{
cout<<"-1"<<endl;
}
else
{
for(int i=0;i<n;i++)
{
cout<<q[i]<<" ";
}
}
}