前文使用邻接矩阵法和邻接链表法实现了图结构(MatrixGraph 、ListGraph ),本节使用两种算法进行图的遍历
目录
1、定义与概括
图的遍历:从图中的某一顶点出发,沿着一些边访问图中的其它顶点,使得每个顶点最多被访问一次
注意:从某个顶点出发进行遍历,不一定能够访问到图中的所有顶点
图的遍历方式
-广度优先搜索(Breadth First Search)
☛ 以二叉树层次遍历的思想对图进行遍历
-深度优先搜索(Depth First Search)
☛ 以二叉树先序遍历的思想对图进行遍历
2、广度优先搜索算法
广度优先(BFS)
-按层次对图中的顶点进行访问
算法思路
-原料: class LinkQueue<T>;
- 步骤
1. 将起始顶点压入队列中
2. 队头顶点v弹出,判断是否已经标记(标记:转2, 未标记:转3)
3. 标记顶点 v ,并将顶点 v 的邻接顶点压入队列中
4 判断队列是否为空(非空:转2, 空:结束)
广度优先算法示例
编程实验
广度优先算法 Graph::BFS
Graph.h
// V:与顶点相关联数据元素类型,E:与边相关联数据元素(权值)类型
template < typename V, typename E >
class Graph : public Object
{
protected:
/* 将队列转换为数组 */
template < typename T >
DynamicArray<T>* toArray(LinkQueue<T>& queue)
{
DynamicArray<T>* ret = new DynamicArray<T>(queue.length());
if( ret )
{
for(int i=0; i<ret->length(); i++, queue.remove())
{
ret->set(i, queue.front());
}
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret object ...");
}
return ret;
}
public:
// ...
SharedPointer< Array<int> > BFS(int i)
{
DynamicArray<int>* ret = NULL;
if((0 <= i) && (i < vCount()))
{
LinkQueue<int> q;
LinkQueue<int> r;
DynamicArray<bool> visited(vCount());
for(int k=0; k<visited.length(); k++)
{
visited[k] = false;
}
q.add(i); // 往q队列加入初始顶点
while(q.length() > 0)
{
int v = q.front(); // 将队列头部的顶点拿出来
q.remove();
if( !visited[v] ) // 没有被访问过
{
SharedPointer< Array<int> > aj = getAdjacent(v); // 获取v顶点邻接顶点放到数组里
for(int j=0; j<aj->length(); j++)
{
q.add((*aj)[j]); // 将邻接顶点放到q队列
}
r.add(v); // 将v放到return队列
visited[v] = true; // 标记该顶点已被访问过
}
}
ret = toArray(r);
}
else
{
THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
}
return ret;
}
};
main.cpp
#include <iostream>
#include "MatrixGraph.h"
using namespace std;
using namespace DTLib;
int main()
{
MatrixGraph<9, char, int> g;
const char* VD = "ABEDCGFHI";
for(int i=0; i<9; i++)
{
g.setVertex(i, VD[i]);
}
g.setEdge(0, 1, 0);
g.setEdge(1, 0, 0); // 有向图表示无向图,权值0代表无权值
g.setEdge(0, 3, 0);
g.setEdge(3, 0, 0);
g.setEdge(0, 4, 0);
g.setEdge(4, 0, 0);
g.setEdge(1, 2, 0);
g.setEdge(2, 1, 0);
g.setEdge(1, 4, 0);
g.setEdge(4, 1, 0);
g.setEdge(2, 5, 0);
g.setEdge(5, 2, 0);
g.setEdge(3, 6, 0);
g.setEdge(6, 3, 0);
g.setEdge(4, 6, 0);
g.setEdge(6, 4, 0);
g.setEdge(6, 7, 0);
g.setEdge(7, 6, 0);
g.setEdge(7, 8, 0);
g.setEdge(8, 7, 0);
SharedPointer< Array<int> > sa = g.BFS(0);
for(int i=0; i<sa->length(); i++)
{
cout << (*sa)[i] << " "; // 0 1 3 4 2 6 5 7 8
}
return 0;
}
3、深度优先搜索算法
深度优先(DFS)
深度优先算法 (方法一)
-原料: class LinkStack<T>;
-步骤
1. 将起始顶点压入栈中
2. 弹出栈顶顶点 v, 判断是否已经标记(标记:转2, 未标记:转3)
3. 标记顶点,并将顶点 v 的邻接顶点压入栈中
4. 判断栈是否为空(非空:转2, 空:结束)
深度优先算法示例
编程实验
深度优先算法 Graph::DFS
Graph.h
SharedPointer< Array<int> > DFS(int i)
{
DynamicArray<int>* ret = NULL;
if((0 <= i) && (i < vCount()))
{
LinkStack<int> s;
LinkQueue<int> r;
DynamicArray<bool> visited(vCount());
for(int j=0; j<visited.length(); j++)
{
visited[j] = false;
}
s.push(i);
while(s.size() > 0)
{
int v = s.top();
s.pop();
if( !visited[v] )
{
SharedPointer< Array<int> > aj = getAdjacent(v);
for(int j=aj->length()-1; j>=0; j--)
{
s.push((*aj)[j]); // 最先获取到的邻接顶点最后入栈,最先出栈
}
r.add(v);
visited[v] = true;
}
}
ret = toArray(r);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "Index i is invalid ...");
}
return ret;
}
main.cpp
#include <iostream>
#include "MatrixGraph.h"
using namespace std;
using namespace DTLib;
int main()
{
MatrixGraph<9, char, int> g;
const char* VD = "ABEDCGFHI";
for(int i=0; i<9; i++)
{
g.setVertex(i, VD[i]);
}
g.setEdge(0, 1, 0);
g.setEdge(1, 0, 0); // 有向图表示无向图,权值0代表无权值
g.setEdge(0, 3, 0);
g.setEdge(3, 0, 0);
g.setEdge(0, 4, 0);
g.setEdge(4, 0, 0);
g.setEdge(1, 2, 0);
g.setEdge(2, 1, 0);
g.setEdge(1, 4, 0);
g.setEdge(4, 1, 0);
g.setEdge(2, 5, 0);
g.setEdge(5, 2, 0);
g.setEdge(3, 6, 0);
g.setEdge(6, 3, 0);
g.setEdge(4, 6, 0);
g.setEdge(6, 4, 0);
g.setEdge(6, 7, 0);
g.setEdge(7, 6, 0);
g.setEdge(7, 8, 0);
g.setEdge(8, 7, 0);
SharedPointer< Array<int> > sa = g.DFS(0);
for(int i=0; i<sa->length(); i++)
{
cout << (*sa)[i] << " ";
}
return 0;
}
问题 :如何使用二叉树先序遍历的思想遍历图?
递归版深度优先(方法二)
-原图 被划分为 起始顶点 和 子图 G',深度优先遍历原图 就是先访问 再 深度优先遍历子图
定义功能: DFS(graph, vex)
功能: 以顶点 vex 为起始顶点深度优先遍历 graph
编程实验
递归版深度优先算法 DFS(graph, vex)
#include <iostream>
#include "MatrixGraph.h"
using namespace std;
using namespace DTLib;
template < typename V, typename E >
void DFS(Graph<V, E>& g, int v, Array<bool>& visited)
{
if((0 <= v) && (v < g.vCount()))
{
cout << v << endl;
visited[v] = true;
SharedPointer< Array<int> > aj = g.getAdjacent(v);
for(int i=0; i<aj->length(); i++) // v 没有邻接顶点就不会递归调用
{
if( !visited[(*aj)[i]])
{
DFS(g, (*aj)[i], visited); // 从邻接顶点开始深度优先遍历子图
}
}
}
else
{
THROW_EXCEPTION(InvalidParameterException, "Index v is invalid ...");
}
}
template < typename V, typename E >
void DFS(Graph<V, E>& g, int v)
{
DynamicArray<bool> visited(g.vCount());
for(int i=0; i<visited.length(); i++)
{
visited[i] = false;
}
DFS(g, v, visited);
}
int main()
{
MatrixGraph<9, char, int> g;
const char* VD = "ABEDCGFHI";
for(int i=0; i<9; i++)
{
g.setVertex(i, VD[i]);
}
g.setEdge(0, 1, 0);
g.setEdge(1, 0, 0); // 有向图表示无向图,权值0代表无权值
g.setEdge(0, 3, 0);
g.setEdge(3, 0, 0);
g.setEdge(0, 4, 0);
g.setEdge(4, 0, 0);
g.setEdge(1, 2, 0);
g.setEdge(2, 1, 0);
g.setEdge(1, 4, 0);
g.setEdge(4, 1, 0);
g.setEdge(2, 5, 0);
g.setEdge(5, 2, 0);
g.setEdge(3, 6, 0);
g.setEdge(6, 3, 0);
g.setEdge(4, 6, 0);
g.setEdge(6, 4, 0);
g.setEdge(6, 7, 0);
g.setEdge(7, 6, 0);
g.setEdge(7, 8, 0);
g.setEdge(8, 7, 0);
SharedPointer< Array<int> > sa = g.DFS(0);
for(int i=0; i<sa->length(); i++)
{
cout << (*sa)[i] << " ";
}
DFS(g, 0);
return 0;
}
4、小结
广度优先按照 “层次的方式” 对顶点进行访问
广度优先算法的核心是队列的使用
深度优先按照“先序遍历的方式”对顶点进行访问
深度优先算法的核心是栈的使用
深度优先和广度优先的唯一不同在于栈或队列的使用
深度优先算法可以使用递归的方式实现