“图”学习提纲。
依据相关定义:
注意:图不可以为空图。即必须有点(有穷非空),不必须有边
数据结构中一般讨论简单图:不存在顶点到自身的边,不存在重复的边
无向完全图:边数为n(n-1)/2。n为点数
有向完全图:边数为n(n-1)。n为点数
稀疏和稠密是相对而言的模糊概念
带权图/网:权
子图:生成子图
依据点与边的关系:
对无向图:点的度的和=边数×2
对有向图:点的入度的和=点的出度的和=边数;度=入度+出度
有向树
路径,路径长度,回路/环:简单路径,简单回路/环,最短路径/距离
若图有n个点,大于n-1条边,则存在回路
若图有n个点,小于n-1条边,则为非连通图
注意:对无向图,为连通性;对有向图,为强连通性
图的生成树有n-1条边,有n-1条边不一定是图的生成树。n为点数
类型:顺序存储结构
组成:
创建的时间复杂度:
空间复杂度:
适用:稠密图
相关性质:
类型:顺序和链式存储结构;关注点
组成:
创建的时间复杂度:
空间复杂度:
适用:稀疏图
相关性质:
类型:无向图的顺序和链式存储结构;关注边
组成:
空间复杂度:
相关性质:
类型:有向图的顺序和链式存储结构
组成:
创建的时间复杂度:
空间复杂度:
相关性质:
类型:顺序存储结构;关注边
组成:
相关性质:
时间复杂度:依据存储结构
空间复杂度:O(v)。v为点数/辅助空间规模/递归栈规模
伪代码:
bool visited[MAX_VERTEX_NUM]; //访问标记数组
void DFSTraverse(Graph G){ //对图G进行深度优先遍历
for(int v=0;v<G.vexnum;++v)
visited[v] = FALSE; //初始化已访问标记数据
for(int v=0;v<G.vexnum;++v) //本代码是从v=0开始遍历
if(!visited[v])
DFS(G,v);
}
void DFS(Graph G,int v){ //从顶点v出发,深度优先遍历图G
visit(v); //访问顶点v
visited[v] = TRUE; //设已访问标记
for(w = FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
if(!visited[w]){ //w为v的尚未访问的邻接顶点
DFS(G,w);
} //if
}
时间复杂度:依据存储结构
空间复杂度:O(v)。v为点数/辅助空间规模
伪代码:
bool visited[MAX_VERTEX_NUM]; //访问标记数组
bool BFSTraverse(Graph G){ //对图G进行广度优先遍历
for(int i=0;i<G.vexnum;++i)
visited[i] = FALSE; //访问标记数组初始化
InitQueue(Q); //初始化辅助队列Q
for(int i=0;i<G.vexnum;++i){ //从0号顶点开始遍历
if(!visited[i]) //对每个连通分量调用一次BFS
BFS(G,i); //vi未访问过,从vi开始BFS
}
}
void BFS(Graph G,int v){ //从顶点v出发,广度优先遍历图G
visit(v); //访问初始顶点v
visited[v] = TRUE; //对v做已访问标记
EnQueue(Q,v); //顶点v入队列Q
while(!isEmpty(Q)){
DeQueue(Q,v); //顶点v出队列
for(w = FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
//检测v所有邻接点
if(!visited[w]){ //w为v的尚未访问的邻接顶点
visit[w]; //访问顶点w
visited[w] = TRUE; //对w做已访问标记
EnQueue(Q,w); //顶点w入队列
}//if
}//while
}
概念:
性质:
构造算法:基于贪心思想;对无向图
构造算法的伪代码:
GENERIC MST(G){
T=NULL;
while T未形成一棵生成树;
do 找到一条最小代价边(u,v)并且加入T后不会产生回路;
T=T∪(u,v);
}
概述:
过程:选边加点加边
伪代码:
void Prim(G,T){
T=空集; //初始化空树
U={w}; //添加任一顶点w
while((V-U)!=空集){ //若树中不含全部顶点
设(u,v)是使u属于U与v属于(V-U),且权值最小的边;
T=T∪{(u,v)} //边归入树
U=U∪{v}; //顶点归入树
}
时间复杂度:O(n²)。n为点数
空间复杂度:O(n)。n为点数。辅助空间记录标记已访问点和候选边
适用:边稠密图
概述:
过程:加边加点;合并点/连通分量/树
若边为候选边和若点为候选点的另一种描述:若加边不构成回路,则加边加点,否则不加边不加点并转2
伪代码:
void Kruskal(V,T){
T=V; //初始化树T,仅含顶点
numS=n; //连通分量数
while(numS>1){ //若连通分量数大于1
从E中取出权值最小的边(v,u);
if(v和u属于T中不同的连通分量){
T=T∪{(v,u)}; //将此边加入生成树中
numS--; //连通分量数减1
}
}
}
时间复杂度:排序算法中常用堆排序,堆存储边:O(eloge)。e为边数
空间复杂度:O(n)。n为点数。辅助空间并查集记录点的树型结构
适用:边稀疏点稠密图/稀疏图
概念:
算法:
概述:
思想:求单源最短路径:图中某点到其他点的最短路径
求多源最短路径:图中多点到其他点的最短路径。时间复杂度:O(n×n²)=O(n的3次方)。n为点数
过程:选边加点加边
伪代码:
void Dijkstra(MGraph g,int v,int dist[],int path[])
{
int set[maxSize];
int min, i, j, u;
/*从这句开始对各数组进行初始化*/
for(i=0;i<g.n;++i)
{
dist[i]=g.edges[v][i];
set[i]=0;
if(g.edges[v][i]<INF)
path[i]=v;
else
path[i]=-1;
}
set[v]=1;path[v]=-1;
/*初始化结束*/
/*关键操作开始*/
for(i=0;i<g.n-1;++i)
{
min=INF;
/*这个循环每次从剩余顶点中选出一个顶点,通往这个顶点的路径在通往所有剩余顶点的路径中是长度最短的*/
for(j=0;j<g.n;++j)
{
if(set[j]==0&&dist[j]<min)
{
u=j;
min=dist[j];
}
}
set[u]=1; //将选出的顶点并入最短路径中
/*这个循环以刚并入的顶点作为中间点,对所有通往剩余顶点的路径进行检测*/
for(j=0;j<g.n;++j)
{
/*这个if语句判断顶点u的加入是否会出现通往顶点j的更短的路径,如果出现,则改变原来的路径及其长度,否则什么都不做*/
if(set[j]==0&&dist[u]+g.edges[u][j]<dist[j])
{
dist[j]=dist[u]+g.edges[u][j];
path[j]=u;
}
}
}
/*关键操作结束*/
}
/*函数结束时,dist[]数组中存放了v点到其余顶点的最短路径长度,path[]中存放v点到其余各顶点的最短路径*/
时间复杂度:O(n²)。n为点数
空间复杂度:O(n)。n为点数。辅助空间记录点间代价,路径中点的前一点和标记已访问点
适用:非带权图;不存在负权值的带权图
思想:求点间的最短路径
过程:迭代
伪代码:
void Floyd(MGraph *g,int Path[][maxSize],int A[][maxSize])
{ //图g的边矩阵中,用INF来表示两点之间不存在边
int i,j,k;
/*这个双循环对数组A[][]和Path[][]进行了初始化*/
int A[MAXSIZE][MAXSIZE] = { 0 };
for(i=0;i<g->n;++i)
{
for(j=0;j<g->n;++j)
{
A[i][j]=g->edges[i][j];
Path[i][j]=-1;
}
}
/*下面这个三层循环是本算法的主要操作,完成了以k为中间点对所有的顶点对(i,j)进行检测和修改*/
for(k=0;k<g->n;++k)
for(i=0;i<g->n;++i)
for(j=0;j<g->n;++j)
{
if(A[i][j]>A[i][k]+A[k][j])
Path[i][j] = k;
}
}
时间复杂度:O(n的3次方)。n为点数
空间复杂度:O(n²)。辅助空间记录代价和路径
适用:非带权图;可存在负权值,负权值边不成回路的带权图
顶点表示活动的网/活动在顶点上的网(AOV网):
有向无环图可类比树,使用表达式描述
算法:拓扑排序
概念:
过程:
伪代码:
int TopSort(AGraph *G)
{
int i,j,n=0;
int stack[maxSize],top=-1; //定义并初始化栈
ArcNode *p;
/*这个循环将图中入度为0的顶点入栈*/
for(i=0;i<G->n;++i) //图中顶点从0开始编号
if(G->adjlist[i].count==0)
stack[++top]=i;
/*关键操作开始*/
while(top!=-1)
{
i=stack[top--]; //顶点出栈
++n; //计数器加一,统计当前顶点
cout<<i<<" "; //输出当前顶点
p=G->adjlist[i].firstarc;
/*这个循环实现了将所有由此顶点引出的边所指向的顶点的入度都减少1,并将这个过程中入度变为0的顶点入栈*/
while(p!=NULL)
{
j=p->adjvex;
--(G->adjlist[j].count);
if(G->adjlist[i].count==0)
{
stack[++top]=j;
}
p=p->nextarc;
}
}
/*关键操作结束*/
if(n==G->n)
return 1;
else
return 0;
}
时间复杂度:依据存储结构
空间复杂度:O(n)。n为点数。辅助空间栈记录前驱/入度为0点
其他:
边表示活动的网/活动在边上的网(AOE网):
注意:AOV网的边无权值
AOE网的性质:
相关概念:
注意:
算法:求关键路径
算法的参量(重点理解):
过程:
“图”学习提纲。