zoukankan      html  css  js  c++  java
  • 图的遍历

    图的遍历和树的遍历类似,希望从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫图的遍历。
     
        对于图的遍历来说,如何避免因回路陷入死循环,就需要科学地设计遍历方案,通过有两种遍历次序方案:深度优先遍历和广度优先遍历。
     
    1. 深度优先遍历
     
        深度优先遍历,也有称为深度优先搜索,简称DFS。其实,就像是一棵树的前序遍历。
     
        它从图中某个结点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。
     
        若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。
     
        我们用邻接矩阵的方式,则代码如下所示。
     
     
    #define MAXVEX  100     //最大顶点数
    typedef int Boolean;            //Boolean 是布尔类型,其值是TRUE 或FALSE
    Boolean visited[MAXVEX];        //访问标志数组
    #define TRUE 1
    #define FALSE 0
     
    //邻接矩阵的深度优先递归算法
    void DFS(Graph g, int i)
    {
        int j;
        visited[i] = TRUE;
        printf("%c ", g.vexs[i]);                           //打印顶点,也可以其他操作
        for(j = 0; j < g.numVertexes; j++)
        {
            if(g.arc[i][j] == 1 && !visited[j])
            {
                DFS(g, j);                  //对为访问的邻接顶点递归调用
            }
        }
    }
     
    //邻接矩阵的深度遍历操作
    void DFSTraverse(Graph g)
    {
        int i;
        for(i = 0; i < g.numVertexes; i++)
        {
            visited[i] = FALSE;         //初始化所有顶点状态都是未访问过状态
        }
        for(i = 0; i < g.numVertexes; i++)
        {
            if(!visited[i])             //对未访问的顶点调用DFS,若是连通图,只会执行一次
            {
                DFS(g,i);
            }
        }
    }
    -----------------------------------------------
     
    如果使用的是邻接表存储结构,其DFSTraverse函数的代码几乎是相同的,只是在递归函数中因为将数组换成了链表而有不同,代码如下。
     
    //邻接表的深度递归算法
    void DFS(GraphList g, int i)
    {
        EdgeNode *p;
        visited[i] = TRUE;
        printf("%c ", g->adjList[i].data);   //打印顶点,也可以其他操作
        p = g->adjList[i].firstedge;
        while(p)
        {
            if(!visited[p->adjvex])
            {
                DFS(g, p->adjvex);           //对访问的邻接顶点递归调用
            }
            p = p->next;
        }
    }
     
    //邻接表的深度遍历操作
    void DFSTraverse(GraphList g)
    {
        int i;
        for(i = 0; i < g.numVertexes; i++)
        {
            visited[i] = FALSE;
        }
        for(i = 0; i < g.numVertexes; i++)
        {
            if(!visited[i])
            {
                DFS(g, i);
            }
        }
    }
         对比两个不同的存储结构的深度优先遍历算法,对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找某个顶点的邻接点需要访问矩阵中的所有元素,因为需要O(n2)的时间。而邻接表做存储结构时,找邻接点所需的时间取决于顶点和边的数量,所以是O(n+e)。显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。
     
    -------------------------------------------------------------------------------------
     
    2. 广度优先遍历
     
        广度优先遍历,又称为广度优先搜索,简称BFS。图的广度优先遍历就类似于树的层序遍历了。
     
        邻接矩阵做存储结构时,广度优先搜索的代码如下。
     
    //邻接矩阵的广度遍历算法
    void BFSTraverse(Graph g)
    {
        int i, j;
        Queue q;
        for(i = 0; i < g.numVertexes; i++)
        {
            visited[i] = FALSE;
        }
        InitQueue(&q);
        for(i = 0; i < g.numVertexes; i++)//对每个顶点做循环
        {
            if(!visited[i])               //若是未访问过
            {
                visited[i] = TRUE;
                printf("%c ", g.vexs[i]); //打印结点,也可以其他操作
                EnQueue(&q, i);           //将此结点入队列
                while(!QueueEmpty(q))     //将队中元素出队列,赋值给
                {
                    int m;
                    DeQueue(&q, &m);
                    for(j = 0; j < g.numVertexes; j++)
                    {
                        //判断其他顶点若与当前顶点存在边且未访问过
                        if(g.arc[m][j] == 1 && !visited[j])
                        {
                            visited[j] = TRUE;
                            printf("%c ", g.vexs[j]);
                            EnQueue(&q, j);
                        }
                    }
                }
            }
        }
    }  
     1
     
    ------------------------------------------
     
    对于邻接表的广度优先遍历,代码与邻接矩阵差异不大, 代码如下。
     
    //邻接表的广度遍历算法
    void BFSTraverse(GraphList g)
    {
        int i;
        EdgeNode *p;
        Queue q;
        for(i = 0; i < g.numVertexes; i++)
        {
            visited[i] = FALSE;
        }
        InitQueue(&q);
        for(i = 0; i < g.numVertexes; i++)
        {
            if(!visited[i])
            {
                visited[i] = TRUE;
                printf("%c ", g.adjList[i].data);   //打印顶点,也可以其他操作
                EnQueue(&q, i);
                while(!QueueEmpty(q))
                {
                    int m;
                    DeQueue(&q, &m);
                    p = g.adjList[m].firstedge;     找到当前顶点边表链表头指针
                    while(p)
                    {
                        if(!visited[p->adjvex])
                        {
                            visited[p->adjvex] = TRUE;
                            printf("%c ", g.adjList[p->adjvex].data);
                            EnQueue(&q, p->adjvex);
                        }
                        p = p->next;
                    }
                }
            }
        }
    }  
     
     
    对比图的深度优先遍历与广度优先遍历算法,会发现,它们在时间复杂度上是一样的,不同之处仅仅在于对顶点的访问顺序不同。
     
    可见两者在全图遍历上是没有优劣之分的,只是不同的情况选择不同的算法。
  • 相关阅读:
    linux通过内容找文件的位置
    Mac通过端口查进程号
    执行brew update后显示Permission denied
    Linux通过进程ID查看文件路径
    Linux通过端口查进程号
    linux 如何查找命令的路径
    lua中冒号(:)与点号(.)的区别
    HTTP返回码301与302的区别
    vue使用animate.css类库实现动画
    vue过渡动画
  • 原文地址:https://www.cnblogs.com/yujon/p/5467612.html
Copyright © 2011-2022 走看看