图的遍历和树的遍历类似,我们希望从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历。
由于图比较复杂,我们在遍历的过程中把访问过的顶点打上标记,以避免访问多次而不自知。具体办法是设置一个访问数组visited[n],n是图中顶点的个数,初始值为0,访问过后设置为1。
遍历方案:
1.深度优先遍历(DepthFirstSearch),也称深度优先搜索,简称DFS
2.广度优先遍历(BreadthFirstSearch),又称为广度优先搜索,简称BFS
1.深度优先遍历
深度优先遍历其实就是一个递归的过程,就像是一棵树的前序遍历。
它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
private bool[] visited;
/// <summary>
/// 邻接矩阵的深度优先遍历
/// </summary>
/// <param name="gl"></param>
public void DFSTraverse()
{
visited = new bool[maxNumVertex];
for (int i = 0; i < vertexs.Length; i++)
{
visited[i] = false; //初始化所有顶点都是未访问的状态
}
for (int i = 0; i < vertexs.Length; i++)
{
//对未访问过的顶点调用DFS,若是连通图,只会执行一次
if (!visited[i])
{
DFS(i);
}
}
}
/// <summary>
/// 邻接矩阵的深度优先递归算法
/// </summary>
/// <param name="gl"></param>
/// <param name="i"></param>
private void DFS(int i)
{
visited[i] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(vertexs[i]);
for (int j = 0; j < vertexs.Length; j++)
{
//edges[i, j] == 1表示两顶点是连通的
//如果是权值,可以判断: edges[i,j]!=0 && edges[i,j]! = MaxValue
//MaxValue自己定,可以用int.MaxValue,long.MaxValue,float.MaxValue等
//具体权值用int long 还是float自己定
if (edges[i, j] == 1 && !visited[j])
{
DFS(j);
}
}
}
}
private bool[] visited;
/// <summary>
/// 邻接表的深度优先遍历
/// </summary>
public void DFSTraverse()
{
visited = new bool[adjacencyArr.Length];
for (int i = 0; i < adjacencyArr.Length; i++)
{
visited[i] = false; //初始化所有顶点都是未访问的状态
}
for (int i = 0; i < adjacencyArr.Length; i++)
{
//对未访问过的顶点调用DFS,若是连通图,只会执行一次
if (!visited[i])
{
DFS(i);
}
}
}
/// <summary>
/// 邻接表的深度优先递归算法
/// </summary>
/// <param name="gl"></param>
/// <param name="i"></param>
private void DFS(int i)
{
visited[i] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(adjacencyArr[i].data);
EdgeNode temp = adjacencyArr[i].firstEdge;
while (temp != null)
{
if (!visited[temp.adjvex])
DFS(temp.adjvex);
temp = temp.next;
}
}
}
2.广度优先遍历:
图的深度优先遍历类似树的前序遍历。
图的广度优先遍历类似树的层序遍历。
/// <summary>
/// 邻接矩阵:广度优先遍历
/// </summary>
/// <param name="graph"></param>
public void BFSTraverse()
{
Queue<int> queue = new Queue<int>(); //初始化辅助队列
visited = new bool[maxNumVertex];
for (int i = 0; i < vertexs.Length; i++)
{
visited[i] = false;
}
for (int i = 0; i < vertexs.Length; i++) //对每一个顶点做循环
{
if (!visited[i]) //若是未访问过就处理
{
visited[i] = true; //设置当前顶点访问过
//这里对顶点的操作,这里简单的打印
Debug.Log(vertexs[i]);
queue.Enqueue(i); //将此顶点入队列
while (queue.Count > 0) //当前队列有元素
{
i = queue.Dequeue(); //出队列
for (int j = 0; j < vertexs.Length; j++)
{
//edges[i, j] == 1表示两顶点是连通的
//如果是权值,可以判断: edges[i,j]!=0 && edges[i,j]! = MaxValue
//MaxValue自己定,可以用int.MaxValue,long.MaxValue,float.MaxValue等
//具体权值用int long 还是float自己定
if (edges[i, j] == 1 && !visited[j])
{
visited[j] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(vertexs[j]);
queue.Enqueue(j);
}
}
}
}
}
}
/// <summary>
/// 邻接表:广度优先遍历
/// </summary>
/// <param name="gl"></param>
public void BFSTraverse()
{
visited = new bool[adjacencyArr.Length];
Queue<int> queue = new Queue<int>(); //初始化辅助队列
EdgeNode p;
for (int i = 0; i < adjacencyArr.Length; i++)
{
visited[i] = false;
}
for (int i = 0; i < adjacencyArr.Length; i++) //对每一个顶点做循环
{
if (!visited[i]) //若是未访问过就处理
{
visited[i] = true; //设置当前顶点访问过
//这里对顶点的操作,这里简单的打印
Debug.Log(adjacencyArr[i].data);
queue.Enqueue(i); //将此顶点入队列
while (queue.Count > 0) //当前队列有元素
{
i = queue.Dequeue(); //出队列
p = adjacencyArr[i].firstEdge; //找到当前顶点边表链表头指针
while (p != null)
{
//此顶点未访问过
if (!visited[p.adjvex])
{
visited[p.adjvex] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(adjacencyArr[p.adjvex].data);
queue.Enqueue(p.adjvex); //将此顶点入队列
}
p = p.next; //指针指向下一个邻接点
}
}
}
}
}
深度优先遍历与广度优先遍历算法,在时间复杂度上是一样的,不同之处仅仅对顶点访问的顺序不同。
在全图遍历上没有优劣之分,只是视不同的情况选择不同的算法。
源码:
邻接矩阵:
/// <summary>
/// 泛型邻接矩阵
/// </summary>
public class AdjacentMatrix<T>
{
#region 数据
//数据
private T[] vertexs; //顶点数组:数据
private int[,] edges; //边数组:权值
private int maxNumVertex; //最大顶点数
#endregion
#region 操作
/// <summary>
/// 创建无向网图的邻接矩阵表示
/// </summary>
/// <param name="vertexs">顶点数据</param>
/// <param name="edges">边数据:权值</param>
public AdjacentMatrix(T[] vertexs, int[,] edges)
{
maxNumVertex = vertexs.Length;
this.vertexs = new T[maxNumVertex];
this.edges = new int[maxNumVertex, maxNumVertex];
Array.Copy(vertexs, this.vertexs, vertexs.Length);
Array.Copy(edges, this.edges, edges.Length);
}
/// <summary>
/// 创建无向网图的邻接矩阵表示
/// </summary>
/// <param name="vertexs">顶点数据</param>
/// <param name="edges">边数据:权值</param>
/// <param name="maxNumVertex">最大顶点数组容量</param>
public AdjacentMatrix(T[] vertexs, int[,] edges, int maxNumVertex)
{
maxNumVertex = maxNumVertex < vertexs.Length ? vertexs.Length : maxNumVertex;
this.vertexs = new T[maxNumVertex];
this.edges = new int[maxNumVertex, maxNumVertex];
this.maxNumVertex = maxNumVertex;
Array.Copy(vertexs, this.vertexs, vertexs.Length);
Array.Copy(edges, this.edges, edges.Length);
}
private bool[] visited;
/// <summary>
/// 邻接矩阵的深度优先遍历
/// </summary>
/// <param name="gl"></param>
public void DFSTraverse()
{
visited = new bool[maxNumVertex];
for (int i = 0; i < vertexs.Length; i++)
{
visited[i] = false; //初始化所有顶点都是未访问的状态
}
for (int i = 0; i < vertexs.Length; i++)
{
//对未访问过的顶点调用DFS,若是连通图,只会执行一次
if (!visited[i])
{
DFS(i);
}
}
}
/// <summary>
/// 邻接矩阵的深度优先递归算法
/// </summary>
/// <param name="gl"></param>
/// <param name="i"></param>
private void DFS(int i)
{
visited[i] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(vertexs[i]);
for (int j = 0; j < vertexs.Length; j++)
{
//edges[i, j] == 1表示两顶点是连通的
//如果是权值,可以判断: edges[i,j]!=0 && edges[i,j]! = MaxValue
//MaxValue自己定,可以用int.MaxValue,long.MaxValue,float.MaxValue等
//具体权值用int long 还是float自己定
if (edges[i, j] == 1 && !visited[j])
{
DFS(j);
}
}
}
/// <summary>
/// 邻接矩阵:广度优先遍历
/// </summary>
/// <param name="graph"></param>
public void BFSTraverse()
{
Queue<int> queue = new Queue<int>(); //初始化辅助队列
visited = new bool[maxNumVertex];
for (int i = 0; i < vertexs.Length; i++)
{
visited[i] = false;
}
for (int i = 0; i < vertexs.Length; i++) //对每一个顶点做循环
{
if (!visited[i]) //若是未访问过就处理
{
visited[i] = true; //设置当前顶点访问过
//这里对顶点的操作,这里简单的打印
Debug.Log(vertexs[i]);
queue.Enqueue(i); //将此顶点入队列
while (queue.Count > 0) //当前队列有元素
{
i = queue.Dequeue(); //出队列
for (int j = 0; j < vertexs.Length; j++)
{
//edges[i, j] == 1表示两顶点是连通的
//如果是权值,可以判断: edges[i,j]!=0 && edges[i,j]! = MaxValue
//MaxValue自己定,可以用int.MaxValue,long.MaxValue,float.MaxValue等
//具体权值用int long 还是float自己定
if (edges[i, j] == 1 && !visited[j])
{
visited[j] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(vertexs[j]);
queue.Enqueue(j);
}
}
}
}
}
}
#endregion
}
邻接表:
/// <summary>
/// 边表结点
/// </summary>
public class EdgeNode
{
public int adjvex; //邻接点域,存储该顶点对应的下标
public int weight; //用于存储权值,对于非网图可以不需要(关键路径需要权值)
public EdgeNode next; //链域,指向下一个邻接点
}
/// <summary>
/// 顶点表结点
/// </summary>
public class VertexNode<T>
{
public int inDegree; //入度:拓扑排序用
public T data; //顶点域,存储顶点信息
public EdgeNode firstEdge; //边表头指针
}
/// <summary>
/// 泛型邻接表:一维数组 + 链表(数组和链表自行提供)
/// </summary>
public class AdjacencyList<T>
{
private VertexNode<T>[] adjacencyArr; //顶点表
/// <summary>
/// 说明:没有按书上写,需要的数据自行提供
/// </summary>
/// <param name="vertexData">顶点数据</param>
public AdjacencyList(VertexNode<T>[] vertexData)
{
adjacencyArr = vertexData;
}
/// <summary>
/// 说明:没有按书上写,需要的数据自行提供,包括顶点数据和边表数据
/// </summary>
/// <param name="vertexData">顶点数据</param>
/// <param name="edgeData">边表数据</param>
public AdjacencyList(VertexNode<T>[] vertexData, EdgeNode[] edgeData)
{
adjacencyArr = new VertexNode<T>[vertexData.Length];
for (int i = 0; i < vertexData.Length; i++) //建立顶点表
{
adjacencyArr[i] = vertexData[i]; //顶点数据
adjacencyArr[i].firstEdge = edgeData[i]; //建立边表
}
}
private bool[] visited;
#region 操作
/// <summary>
/// 邻接表的深度优先遍历
/// </summary>
public void DFSTraverse()
{
visited = new bool[adjacencyArr.Length];
for (int i = 0; i < adjacencyArr.Length; i++)
{
visited[i] = false; //初始化所有顶点都是未访问的状态
}
for (int i = 0; i < adjacencyArr.Length; i++)
{
//对未访问过的顶点调用DFS,若是连通图,只会执行一次
if (!visited[i])
{
DFS(i);
}
}
}
/// <summary>
/// 邻接表的深度优先递归算法
/// </summary>
/// <param name="gl"></param>
/// <param name="i"></param>
private void DFS(int i)
{
visited[i] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(adjacencyArr[i].data);
EdgeNode temp = adjacencyArr[i].firstEdge;
while (temp != null)
{
if (!visited[temp.adjvex])
DFS(temp.adjvex);
temp = temp.next;
}
}
/// <summary>
/// 邻接表:广度优先遍历
/// </summary>
/// <param name="gl"></param>
public void BFSTraverse()
{
visited = new bool[adjacencyArr.Length];
Queue<int> queue = new Queue<int>(); //初始化辅助队列
EdgeNode p;
for (int i = 0; i < adjacencyArr.Length; i++)
{
visited[i] = false;
}
for (int i = 0; i < adjacencyArr.Length; i++) //对每一个顶点做循环
{
if (!visited[i]) //若是未访问过就处理
{
visited[i] = true; //设置当前顶点访问过
//这里对顶点的操作,这里简单的打印
Debug.Log(adjacencyArr[i].data);
queue.Enqueue(i); //将此顶点入队列
while (queue.Count > 0) //当前队列有元素
{
i = queue.Dequeue(); //出队列
p = adjacencyArr[i].firstEdge; //找到当前顶点边表链表头指针
while (p != null)
{
//此顶点未访问过
if (!visited[p.adjvex])
{
visited[p.adjvex] = true;
//这里对顶点的操作,这里简单的打印
Debug.Log(adjacencyArr[p.adjvex].data);
queue.Enqueue(p.adjvex); //将此顶点入队列
}
p = p.next; //指针指向下一个邻接点
}
}
}
}
}
#endregion
}