数据结构学习:图的基本操作
图的基本操作:
• Adjacent(G,x,y):判断图G是否存在边<x, y>或(x, y)。
• Neighbors(G,x):列出图G中与结点x邻接的边。
• InsertVertex(G,x):在图G中插入顶点x。
• DeleteVertex(G,x):从图G中删除顶点x。
• AddEdge(G,x,y):若无向边(x, y)或有向边<x, y>不存在,则向图G中添加该边。
• RemoveEdge(G,x,y):若无向边(x, y)或有向边<x, y>存在,则从图G中删除该边。
• FirstNeighbor(G,x):求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1。
• NextNeighbor(G,x,y):假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1。
• Get_edge_value(G,x,y):获取图G中边(x, y)或<x, y>对应的权值。
• Set_edge_value(G,x,y,v):设置图G中边(x, y)或<x, y>对应的权值为v。
广度优先遍历
bool visited[Max_Vertex_Num];//访问标记数组。初始值都为false
void BFS(Grapg G,int v) //从顶点V开始广度优先遍历图G
{
visit(v); //访问顶点v
visited[v] = True; //标记顶点v为True,记为已访问
EnQuene(Q,v); //将顶点v入队
while(!isEmpty(Q))
{//当队列Q不为空时
DeQuene(Q,v); //队头顶点出队,并返回值为v
for(w = firstNeighbor(G,v); w >= 0;w = NextNeighbor(G,v,w))
{//定义w为v在图G中的第一个临界点
//每循环一次,w为除w之外顶点v的下一个邻接点的顶点号,若w是v的最后一个邻接点,则返回-1
if(!visited[w])//当w为False,即未访问过时
{
visit(w); //访问w顶点
visited[w] = True;//标记w顶点为已访问
EnQuene(Q,w);//将w顶点入队
}
}
}
}
同⼀个图的邻接矩阵表示⽅式唯⼀,因此⼴度优先遍历序列唯⼀
同⼀个图邻接表表示⽅式不唯⼀,因此⼴度优先遍历序列不唯⼀
该广度优先遍历存在问题:当图是非联通图的话,无法遍历完所有结点
需要对该算法进行优化
改进后
将所有顶点存入数组,然后对其进行广度优先遍历
void BFSTravares(Grapg G)
{
for(int i = 0;i < G.vexnum;++i)
{
visited[i] = False; //初始化标记数组
}
InitQuene(Q); //初始化队列Q
for(i = 0;i < G.vexnum;++i)//从0号顶点开始遍历
{
if(!visited[i]) //若该顶点为访问过,则去访问该顶点以及其所连通的顶点
BFS(G,i);
}
}
// 广度优先遍历
bool visited[Max_Vertex_Num];//访问标记数组。初始值都为false
void BFS(Grapg G,int v) //从顶点V开始广度优先遍历图G
{
visit(v); //访问顶点v
visited[v] = True; //标记顶点v为True,记为已访问
EnQuene(Q,v); //将顶点v入队
while(!isEmpty(Q))
{//当队列Q不为空时
DeQuene(Q,v); //队头顶点出队,并返回值为v
for(w = firstNeighbor(G,v); w >= 0;w = NextNeighbor(G,v,w))
{//定义w为v在图G中的第一个临界点
//每循环一次,w为除w之外顶点v的下一个邻接点的顶点号,若w是v的最后一个邻接点,则返回-1
if(!visited[w])//当w为False,即未访问过时
{
visit(w); //访问w顶点
visited[w] = True;//标记w顶点为已访问
EnQuene(Q,w);//将w顶点入队
}
}
}
}
复杂度分析:
空间复杂度:最坏情况,辅助队列⼤⼩为 O(|V|)
邻接矩阵存储的图:
访问 |V| 个顶点需要O(|V|)的时间
查找每个顶点的邻接点都需要O(|V|)的时间,⽽总共有|V|个顶点
时间复杂度= O(|V|^2 )
邻接表存储的图:
访问 |V| 个顶点需要O(|V|)的时间
查找各个顶点的邻接点共需要O(|E|)的时间,
时间复杂度= O(|V|+|E|)
⼴度优先⽣成树由⼴度优先遍历过程确定。由于邻接表的表示⽅式不唯⼀,因此基于邻接表的⼴度优先⽣成树也不唯⼀。
对⾮连通图的⼴度优先遍历,可得到⼴度优先⽣成森林
图的深度优先遍历
图的深度优先遍历类似于树的先根遍历
// 宽度优先遍历,使用递归的方法,将一条路径上的结点全部遍历
void DFS(Grapg G,int v)
{
visit(v);
visited[v] = True;
for(w = FirstNeighbor(G,v);w>=0;w = NextNeighbor(G,v,w))
{
if(!visited[w])
DFS(G,w);
}
}
//为防止图不是连通图,将所有结点存储并依次遍历
void DFSTravares(Grapg G)
{
for(int i = 0;i < G.vexnum;++i)
{
visited[i] = False; //初始化标记数组
}
for(i = 0;i < G.vexnum;++i)//从0号顶点开始遍历
{
if(!visited[i]) //若该顶点为访问过,则去访问该顶点以及其所连通的顶点
DFS(G,i);
}
}
空间复杂度:来⾃递归函数调⽤栈,最坏情况,递归深度为O(|V|)
时间复杂度=访问各结点所需时间+探索各条边所需时间
邻接矩阵存储的图:
访问 |V| 个顶点需要O(|V|)的时间
查找每个顶点的邻接点都需要O(|V|)的时间,⽽总共有|V|个顶点
时间复杂度= O(|V|^2 )
邻接表存储的图:
访问 |V| 个顶点需要O(|V|)的时间
查找各个顶点的邻接点共需要O(|E|)的时间,
时间复杂度= O(|V|+|E|)
同⼀个图的邻接矩阵表示⽅式唯⼀,因此深度优先遍历序列唯⼀
同⼀个图邻接表表示⽅式不唯⼀,因此深度优先遍历序列不唯⼀
同⼀个图的邻接矩阵表示⽅式唯⼀,因此深度优先遍历序列唯⼀,深度优先⽣成树也唯⼀
同⼀个图邻接表表示⽅式不唯⼀,因此深度优先遍历序列不唯⼀,深度优先⽣成树也不唯⼀
对⽆向图进⾏BFS/DFS遍历
调⽤BFS/DFS函数的次数=连通分量数
对于连通图,只需调⽤1次 BFS/DFS
对有向图进⾏BFS/DFS遍历
调⽤BFS/DFS函数的次数要具体问题具体分析
若起始顶点到其他各顶点都有路径,则只需调⽤1次BFS/DFS 函数
对于强连通图,从任⼀结点出发都只需调⽤1次 BFS/DFS