文章目录
1.导言
2.思维导图
3.图的定义
- 顶点ii的出度:第ii行1的个数。顶点ii的入度,第ii列1的个数。
图由顶点集V(G)和边集E(G)组成,记为G=(V,E)。其中E(G)是边的有限集合,边是顶点的无序对(无向图)或有序对(有向图)。 - 对有向图来说,E(G)是有向边(也称弧(Arc))的有限集合,弧是顶点的有序对,v、w是顶点, v为弧尾(箭头根部),w为弧头(箭头处)。
- 对无向图来说,E(G)是边的有限集合,边是顶点的无序对,记为(v, w)或者(w, v),并且(v, w)=(w,v)。
- 顶点的度、入度、出度
顶点v的度:与v相关联的边的数目;
顶点v的出度:以v为起点有向边数;
顶点v的入度:以v为终点有向边数。 - 路径与回路
简单路径:序列中顶点不重复出现的路径
简单回路:序列中第一个顶点和最后一个顶点相同的路径 - 连通图(强连通图):在无(有)向图中,若对任何两个顶点v、u都存在从v到u的路径,则称图G为连通图(强联通图)。
- 极大连通子图:该子图是G连通子图,将G的任何不在该子图的顶点加入,子图将不再连通。
极小连通子图:该子图是G的连通子图,在该子图中删除任何一条边,子图都将不再连通。
4.图的存储结构
邻接矩阵
- 概念:用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵。用邻接矩阵表示图,很容易确定图中任意两个顶点是否有边相连。邻接矩阵分为有向图邻接矩阵和无向图邻接矩阵。对无向图(无向简单图)而言,邻接矩阵一定是对称的,而且对角线一定为零,有向图则不一定如此。
- 邻接矩阵结构体定义
#define MAXV 最大顶点个数
//声明顶点类型
typedef struct {
int number;/*顶点编号*/
Info Type info;/*顶点其他信息*/
}VertexType;
//声明邻接矩阵类型
typedef struct {
int edges[MAXV][MAXV];/*邻接矩阵*/
int n, e;/*顶点数、边数*/
VertexType vexs[MAXV];/*顶点信息*/
}MatGraph;
- (1)无向图:
- (2)有向图:
- 建图
void CreateMGraph(MGraph& g, int n, int e)//邻接矩阵建图
{
//n顶点个数,e边个数
int i, j;
int a, b;
for (i = 1; i <= n; i++) //注意输入的起始顶点值从0还是1开始
for (j = 1; j <= n; j++)
g.edges[i][j] = 0;
for (i = 0; i < e; i++)
{
cin >> a >> b;
g.edges[a][b] = 1;
g.edges[b][a] = 1;
}
g.n = n;
g.e = e;
}
邻接表
- 概念:邻接表是数组和链表的结合。对于每个顶点都建立一个单链表存储该顶点所有的邻接点。然后将定义一个结构体VNode,里面保存顶点邻接点的链表和顶点其他信息。设置VNode类型的结构体数组AdjGraph[]就可以保存图中所有顶点的邻接点,达到保存图中所有边的目的。结构体数组AdjGraph[]即为邻接表。
- 邻接表的结构体定义
typedef struct ANode //边结点;
{
int adjvex;//指向该边的终点编号;
struct ANode*nextarc;//指向下一个邻接点;
INfoType info;//保存该边的权值等信息;
}ArcNode;
typedef struct //头结点
{
int data;//顶点;
ArcNode *firstarc;//指向第一个邻接点;
}VNode;
typedef struct
{
VNode adjlist[MAX];//邻接表;
int n,e;//图中顶点数n和边数e;
}AdjGraph;
-
(1)无向图:
-
(2)有向图:
-
邻接矩阵和邻接表表示图的区别
- 思路:
1、在邻接矩阵表示中,无向图的邻接矩阵是对称的。邻接矩阵需要申请一个二维数组,空间复杂度为O(n2)
2、在邻接表的表示中,无向图的同一条边在邻接表中存储的两次。因为共有e条边和n个结点,需要开辟n个空间来保存结点,e个空间来保存e条边信息,所以,创建邻接表的空间复杂度为O(n+e)
- 思路:
5.图的遍历
深度优先遍历
- 概念:
1、首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;
2、当没有未访问过的顶点时,则回到上一个顶点,继续试探别的顶点,直至所有的顶点都被访问过。 - 代码
int visited[MaxN]={
0}; //记录顶点的访问状态
int result[MaxN]; //result数组记录DFS遍历结果
int N,E,k;// N为顶点数,E为边数,k记录遍历结果下标
void DFS(int v){
visited[v]=1;
result[k++]=v;
for (int i=0;i<N;i++){
if (G[v][i]==1 && visited[i]==0)
DFS(i);
}
}
int main(){
for (int i=0;i<N;i++){
k = 0;
if (visited[i]==0){
DFS(i);
//用{}打印出一个连通分量
cout<<"{ ";
for (int j=0;j<k;j++)
cout<<result[j]<<' ';
cout<<"}"<<endl;
}
}
return 0;
}
广度优先遍历
- 思路:有点类似于树的层序遍历,也就是像剥洋葱一样,“一层一层地剥开”。即从一个选定的点出发,将与其直接相连的点都收入囊中,然后依次对这些点去收与其直接相连的点。重复到所有点都被访问然后结束。
- 代码
int visited[MaxN]={
0}; //记录顶点的访问状态
int result[MaxN]; //result数组记录BFS遍历结果
int N,E,k;// N为顶点数,E为边数,k记录遍历结果下标
void BFS(int v){
queue<int> q;
q.push(v);
visited[v]=1;
result[k++]=v;
while