图的存储
图的存储一般有两种方式:邻接矩阵和邻接表
邻接矩阵
设图G(V,E)的顶点标号为0,1,……n-1,则令二维数组G[n][n]的两维分别表示图的顶点标号。
即如果G[i][j]等于1,指顶点i和顶点j之间有边,如果G[i][j]等于0,指顶点i和顶点j之间没有边,
如果为有权图,则令G[i][j]存放边权。
但如果题目中顶点数过大,可能会造成内存超限。
邻接表
图的常用储存结构之一,由表头结点和表结点两部分组成,其中表头结点存储图的各顶点,
表结点用单向链表存储表头结点所对应顶点的相邻顶点(也就是表示了图的边)。
在有向图里表示表头结点指向其它结点(a->b),无向图则表示与表头结点相邻的所有结点(a—b)
//表头结点(表示图的顶点)
struct vnode{
char data; //顶点数据,这里用字符表示
struct arcnode * firstarc; //指针指向第一条边
};
//表结点(表示图的边)
struct arcnode{
int wt; //权重
int adjvex; //顶点下标
struct arcnode *nextarc; //指针指向下一条边
};
typedef struct arcnode * Arc;
//图
struct mgraph{
struct vnode vexs[100];
int vexsnum,arcnum; //顶点数,边数
} *g;
typedef struct mgraph* Graph;
图的遍历
用DFS遍历图
沿着一条路径直到无法继续前进,才退回到路径上离当前顶点最近的还存在未访问分支顶点的岔道口,并前往访问那些未访问的分支节点,直至遍历完成
* 连通分量:在无向图中,如果两个顶点可以互相到达,则称这两个顶点连通,如果图G(V,E)的任意两个顶点都连通,则称图G为连通图,
否则,称图G为非连通图,且称其中的极大连通子图为连通分量。
* 强连通分量:在有向图中,如果两个顶点可以各自通过一条有向路径到达另一个顶点,则称这两个顶点强联通。如果一个图的任意两个顶点都强联通,
则称这个图为强连通图;否则这个图为非强连通图,且称其中的极大连通子图为强联通分量。
伪代码
DFS(u){
vis[u]=true;
for(从u出发能到达的所有顶点v)
if(vis[v]==false)
DFS(v);
}
DFSTrave(G){
for(G的所有顶点u)
if(vis[u]==false)
DFS(u);
}
邻接矩阵实现
const int maxv=1000;
const int inf=1000000;
int n,G[maxv][maxv];
bool vis[maxv]={false};
void dfs(int u,int depth){
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=inf){
dfs(v,depth+1);
}
}
}
void dfstrave(){
for(int u=0;u<n;u++){
if(vis[u]==false){
dfs(u,1);
}
}
}
邻接表实现
vector<int> Adj[maxv];
int n;
bool vis[maxv] = {false};
void dfs(int u,int depth){
vis[u] =true;
for(int i=0;i<Adj[u].size();i++){
int v=Adj[u][i];
if(vis[v]==false){
dfs(v,depth+1);
}
}
}
void dfstrave(){
for(int u=0;u<n;u++){
if(vis[u]==false){
dfs(u,1);
}
}
}
用BFS遍历图
类似树的遍历,遍历图需要使用一个队列,通过反复取出队首顶点,将该顶点可到达的未曾加入过队列的顶点全部入队,直到队列为空时遍历结束。
伪代码
BFS(u){
queue q;
inq[u]=true;
while(q非空){
取出q的队首元素加以访问;
for(从u出发能到达的所有顶点v)
if(inq[v]==false){
将v入队;
inq[v]=true;
}
}
}
BFSTrave(G){
for(G的所有顶点u)
if(inq[u]==false)
{
BFS(u);
}
}
邻接矩阵实现
int n,G[maxv][maxv];
bool inq[maxv]={false};
void BFS(int u){
queue<int> q;
q.push(u);
inq[u]=true;
while(!q.empty()){
int u.q.front();
q.pop();
for(int v=0;v<n;v++){
if(inq[v]==false&&G[u][v]!=inf){
q.push(v);
inq[v]=true;
}
}
}
}
void BFSTrave(){
for(int u=0;u<n;u++){
if(inq[u]==false){
BFS(q);
}
}
}
邻接表实现
vector<int> Adj[maxv];
int n;
bool inq[maxv]={false};
void BFS(int u){
queue<int> q;
q.push(u);
inq[u]=true;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<Adj[u].size();i++){
int v=Adj[u][i];
if(inq[v]==false){
q.push(v);
inq[v]=true;
}
}
}
}
void BFSTrave(){
for(int u=0;u<n;u++){
if(inq[u]==false){
BFS(q);
}
}
}
例子:
#include<bits/stdc++.h>
using namespace std;
typedef struct
{
int edges[100][100];///邻接矩阵
int n;
int e;
}graph;
bool vis[100];///访问数组
void creategraph(graph &G)
{
int i,j;
int s,t;
int v;
for(i=0;i<G.n;i++)
{
for(j=0;j<G.n;j++)
{
G.edges[i][j]=0;///邻接表初始化
}
vis[i]=false;///访问数组初始化
}
for(i=0;i<G.e;i++)
{
cin>>s>>t>>v;///读入顶点数边数和权值
G.edges[s][t]=v;///赋值
}
}
void dfs(graph G,int v)
{
int i;
printf("%d ",v);
vis[v]=true;///访问第v个定点,并将访问数组置为true
for(i=0;i<G.n;i++)
{
if(G.edges[v][i]!=0&&vis[i]==false)
{
dfs(G,i);///如果i未被访问递归调用dfs
}
}
}
void bfs(graph G,int v)
{
queue<int>Q;
printf("%d ",v);
vis[v]=true;
Q.push(v);
while(!Q.empty())
{
int i,j;
i=Q.front();///取队头元素
Q.pop();///队头元素出队
for(j=0;j<G.n;j++)
{///检查所有邻接点
if(G.edges[i][j]!=0&&vis[j]==false)
{
printf("%d ",j);
vis[j]=true;
Q.push(j);
}
}
}
}
int main()
{
int n,e;
while(1)
{
puts("输入图的顶点数和边数:");
cin>>n>>e;
graph G;
G.n=n;
G.e=e;
creategraph(G);
puts("输出深度优先遍历序列:");
dfs(G,0);
puts("\n");
creategraph(G);
puts("输出广度优先遍历序列:");
bfs(G,0);
puts("\n");
}
return 0;
}