数据结构之图
- 图的类型定义和存储结构
- 图的遍历
- 遍历的定义
- 图的特点
- 图常用的遍历
- 深度优先搜索 DFS(DepthFirstSearch)
- 算法实现
- 算法效率分析
- 广度优先搜索 BFS(BreadthFirstSearch)
- 算法实现
- 算法效率分析
- DFS与BFS算法效率比较
图的类型定义和存储结构
图的遍历
遍历的定义
从已给的连通图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算。
图的特点
图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。
那么怎样避免重复访问?
解决思路:设置辅助数组visitel[n],用来标记每个被访问过的顶点。
初始状态visited [i]为0,顶点i 被访问,改 visited [i]为1,防止被多次访问。
图常用的遍历
深度优先搜索 DFS(DepthFirstSearch)
方法:
- 在访问图中某一起始顶点 v后,由 v出发,访问它的任一邻接顶点 w1;
- 再从 w1 出发,访问与w1接但还未被访问过的顶点 w2;
- 然后再从w2出发,进行类似的访问,如此进行下去,直至到达所有的邻接顶点都被访问过的顶点u为止;
- 接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。
- 如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;
- 如果没有,就再退回一步进行搜索。
- 重复上述过程,直到连通图中所有顶点都被访问过为止。
算法实现
采用邻接矩阵表示图的深度优先搜索遍历
void DFS(AMGraph G, int v) //图G为邻接矩阵类型
cout< <v; visited[v] = true;//访问第v个顶点
for(w = 0; w< G.vexnum; w++)//依次检查邻接矩阵v所在的行
if((G.arcs[v][w]!=0)&& (!visited[w]))
DFS(G, w);
//w是v的邻接点,如果w未访问,则递归调用DFS
算法效率分析
用邻接矩阵表示图,遍历图中的每一个顶点都要从头部扫描该顶点所在行,时间复杂度为O(n2)。
广度优先搜索 BFS(BreadthFirstSearch)
方法:
从图的某一节点出发,首先依次访问该节点的多有邻接节点Vi1,V2i,…再按照这些顶点被访问的先后次序,依次访问与他们相连接的所有被访问的顶点;
重复此过程,直到所有顶点均被访问为止。
算法实现
void BFS (Graph G, int v){//按广度优先非递归遍历连通图G
cout< <v; visited[v] = true;//访问第v个顶点
InitQueue(Q);//辅助队列Q初始化,置空
EnQueue(Q v);//v进队
while(!QueueEmpty(Q)){//队列非空
DeQueue(Q, u);//队头元素出队并置为u
for(w = FirstAdjVex(G, u); w>=0; w = NextAdjVex(G, u, w))
if(!visited[w]){ //w为u的尚未访问的邻接顶点
cout< <w; visited[w] = true;
EnQueue(Q, w);//w进队3
}
}
}//BFS
算法效率分析
如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要循环检测矩阵的整整一行(n个元素),总的时间代价为O(n2)。
用邻接矩阵来表示图,虽然有2e个表节点,但是只需要扫描e个节点即可完成遍历,加上访问n个头节点的时间,时间复杂度为O(n+e)。
DFS与BFS算法效率比较
空间复杂度相同,都是O(n)(借用了堆栈或者队列)。
时间复杂度只与存储结构有关(邻接矩阵或者邻接表),而与搜索路径无关。
参考资料:数据结构与算法基础-王卓老师