图是一种很常见的数据结构,对图的分析首先要从其存储结构谈起,如何在计算机中描述一幅图,我们借助树的思想:
(a)是一个简单的图,(b)是一种描述图的方法叫做邻接矩阵,其中横坐标表示以该顶点为出发,纵坐标表示以该顶点为终点。
如图中aa,bb,cc,dd,ee均可为无穷(也可以是1)表示二者不通(通),而ab,ac等根据图的信息进行赋值,本图为有向有权图,因此赋其权值。
另外一种方法是邻接表法:
这是一个无向图,用一个单链表来表示一个顶点开始的所有路径,因此图上有4个顶点需要4个单链表。
链表的每个结点代表与这个顶点相连接的边,比如第一个链表就代表着01,02,03这三条边,每个结点包含三部分:1.邻接域,表示与顶点相邻接的这个顶点(顶点1);2.链域:表示下一条边的顶点信息(顶点2),3.数据域:存储该边的信息如权值。
但还有一个问题,第一个结点的头由谁来指向如表1中的顶点1,因此我们每个表还需要一个头节点(data,firstadj),其含有数据域:存储所有的顶点信息。邻域:存储顶点(0)指向的第一个点的指针(1),即图中的第一个箭头。
弄清楚了图在计算机中是如何存储之后,我们想访问图中所有点的信息,这种想法称为遍历。图的遍历分为:BFS和DFS。
广度优先搜索(BFS):对于一个连通图,从一个顶点开始,依次访问该顶点所有点邻接点,当访问完之后,再按刚才访问点的顺序再依次访问它们的邻接点,直到所有的点均被访问。因此遍历的过程实际上是一个寻找邻接点的过程。
算法如下:
void BFS(Graph G,int v)
{
for(v=0;v<G.vexmax;v++)
visited[v]=false;
InitQueue(Q);
for(v=0;v<G.vexmax;v++)
if(!visited[v])
{
visit(v);
visited[v]=true;
EnQueue(Q,v);
while(!QueueEmpty)
{
DeQueue(Q,u);
for(w=firstadj(G,u);w;w=nextadj(G,u,w))
{
visit(w);
visited[w]=true;
EnQueue(Q,w);
}
}
}
}
首先初始化一个visited[]数组,用来记录该点是否已经被访问过,初始化均为未访问。然后需要初始化一个队列,用来保存这些邻接点,因为根据BFS的定义要按先后顺序对邻接点进行访问,因此如果你是起始点第一顺位邻接点的邻接点的话你将会比第二顺位邻接点的邻接点更快的访问,这种先进先访问的数据结构即为队列。
1.将访问过的顶点放入队列中
2.队列如果不为空,弹出这个元素,依次找寻这个点的所有邻接点,先访问并存到队列中。
3.如果第一层的邻接点都访问过了,就回到队列判空,再次寻找第二层的邻接点,注意此时由于先进先出的原因,第二层的顺序紧跟着第一层。
深度优先搜索:从某个顶点出发,寻找其邻接点,再寻找该邻接点的邻接点,一直寻到该点的所有邻接点均被访问过,这种朝着纵深的方向访问称为深度遍历。
我们采取递归的思想来解决这个问题:
那么依旧采用前几篇说到递归方法:1.当这件事为1的时候,我们具体干什么:在这里我们做的是访问这个结点的信息。2.做什么样的事n-1次与n次是相同的:从顶点开始遍历与从顶点的第一个邻接点开始遍历是相同的操作。
因此算法如下:
void DFS(Graph G,int v)
{
visited[v]=true;
visit(v);
for(w=firstadj(G,v);w;w=nextadj(G,v,w))
{
if(!visited[w])
DFS(w);
}
}
很好理解,先访问顶点然后置标志位为true,再依次分析其各个邻接点,从一个邻接点开始再次调用DFS。