图的搜索---深度优先搜索和广度优先搜索
确定从一个顶点能到达哪些顶点是在图上经常执行的一种操作。人们可能需要知道在地图上哪些路可以从一个 城镇到达其他城镇,或者从一个机场到其他机场可以走哪条航线。 在图上执行这些操作都用到了查找算法。图上可以执行两种基础查找:深度优先(depth-first)搜索和广度优先 (breadth-first)搜索。
首先写一个图的类,里面包含了一些基本操作,用一个一维数组存储所有的顶点,一个二维数组代表邻接矩阵来处理顶点之间的关系:
public class Vertex
{
public bool wasVisited;
public string label;
public Vertex(string label)
{
this.label = label;
wasVisited = false;
}
}
public class Graph
{
private int NUM_VERTICES = 6;
private Vertex[] vertices;
private int[,] adjMatrix;
int numVerts;
public Graph(int numvertices)
{
NUM_VERTICES = numvertices;
vertices = new Vertex[NUM_VERTICES];
adjMatrix = new int[NUM_VERTICES, NUM_VERTICES];
numVerts = 0;
for (int j = 0; j <= NUM_VERTICES -1; j++)
for (int k = 0; k <= NUM_VERTICES - 1; k++)
adjMatrix[j, k] = 0;
}
public void AddVertex(string label)
{
vertices[numVerts] = new Vertex(label);
numVerts++;
}
public void AddEdge(int start, int eend)
{
adjMatrix[start, eend] = 1;
}
public void ShowVertex(int v)
{
Console.Write(vertices[v].label + " ");
}
}
深度优先搜索
深度优先搜索的含义是沿着一条路径从开始顶点到达最后的顶点,然后原路返回,并且沿着下一条路径达到最 后的顶点,如此继续直到走过所有路径,如下图:
下面先直接亮出深度优先搜索的相关代码,之后再来解释:
private int GetAdjUnvisitedVertex(int v)
{
for (int j = 0; j <= NUM_VERTICES - 1; j++)
if ( (adjMatrix[v,j] == 1) && (vertices[j].wasVisited == false))
return j;
return -1;
}
public void DepthFirstSearch()
{
Stack<int> gStack = new Stack<int>();
vertices[0].wasVisited = true;
ShowVertex(0);
gStack.Push(0);
int v;
while (gStack.Count > 0)
{
v = GetAdjUnvisitedVertex(gStack.Peek());
if (v == -1)
gStack.Pop();
else
{
vertices[v].wasVisited = true;
ShowVertex(v);
gStack.Push(v);
}
}
for (int j = 0; j <= NUM_VERTICES - 1; j++)
vertices[j].wasVisited = false;
}
GetAdjUnvisitedVertex方法是获得未访问的顶点的方法。程序通过邻接矩阵来检查顶点所在的行是否有存储着数值1,如果是1则代表两点之间有边则返回顶点在一维数组中的索引,而如果是0则相反。
DepthFirstSearch就是深度优先搜索算法的主体了,整个过程:
1.首先选取一个起始点,它可能是任何顶点(这里就以最初的节点0为例);访问这个顶点,把它压入一个栈内,并且标记为已访问的;(这个栈储存的就是同一条路径上的顶点,也就是上图中一列一列的顶点)
2.接着用Peek找到栈顶元素的下一个邻接点,标记为已访问的,接着把它入栈,下一次再Peek就找到它的下一个邻接点,以此类推直到没有下一个为止。
3.然后一个个出栈,直到弹出至只剩你首先选取的那个顶点为止,发现它又可以找到一个新的邻接点,然后再重复步骤2
广度优先搜索
广度优先搜索算法会从第一个顶点开始尝试访问所有可能在第一个顶点附近的顶点。从本质上说,这种搜索在图上的移动是逐层进行的,首先会检查与第一个顶点相邻的层,然后逐步向下检查远离初始顶点的层。如下图:
public void BreadthFirstSearch()
{
Queue<int> gQueue = new Queue<int>();
vertices[0].wasVisited = true;
ShowVertex(0);
gQueue.Enqueue(0);
int vert1, vert2;
while (gQueue.Count > 0)
{
vert1 = gQueue.Dequeue();
vert2 = GetAdjUnvisitedVertex(vert1);
while (vert2 != -1)
{
vertices[vert2].wasVisited = true;
ShowVertex(vert2);
gQueue.Enqueue(vert2);
vert2 = GetAdjUnvisitedVertex(vert1);
}
}
for (int i = 0; i <= NUM_VERTICES - 1; i++)
vertices[i].wasVisited = false;
}
步骤:
1.同样的选取任意一个顶点入队列并作为当前顶点,访问离当前顶点最近的顶点(这里称为层级1的点),标记一访问,与此同时入队它的下一个邻接点(层级2的点)
2.在层级1的顶点全部访问完毕后,从队列中出队一个顶点代替之前选择的节点作为当前顶点继续重复步骤1
上一篇: 排序及消除重复数字 acm模式
下一篇: C语言中的小知识