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

    图的遍历

    图的遍历是指从图中的某一顶点出发,按照一定的策略访问图中的每一个顶点。当然,每个顶点有且只能被访问一次。

    在图的遍历中,深度优先和广度优先是最常使用的两种遍历方式。这两种遍历方式对无向图和有向图都是适用的,并且都是从指定的顶点开始遍历的。

    先看下两种遍历方式的遍历规则:深度优先遍历广度优先遍历

    说明:以下的深度优先遍历和广度优先遍历都是针对于有向图。

    深度优先遍历

        深度优先遍历也叫深度优先搜索(Depth First Search)。

     它的遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。

        具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,则下一个遍历的顶点是V0的第一个邻接点Vi,接着遍历Vi的第一个邻接点Vj,……直到所有的顶点都被访问过。

        所谓的第一个是指在某种存储结构中(邻接矩阵和邻接表),所有邻接点中存储位置最近的,通常指的是下标最小的。

    在遍历的过程中有以下两种情况经常出现:

    1. 某个顶点的邻接点都已被访问过的情况,此时需回溯已访问过的顶点。
    2. 图不连通,所有的已访问过的顶点都已回溯完了,仍找不出未被访问的顶点。此时需从下标0开始检测visited[i],找到未被访问的顶点i,从i开始新一轮的深度搜索。
    例如:
    从V0开始遍历
        遍历分析:V0有两个邻接点V1和V2,选择下标最小的V1遍历。接着从V1开始深度遍历,V1只有邻接点V3,也就是没有选的:遍历V3。接着从V3开始遍历,V3只有邻接点V0,而V0已经被遍历过。此时出现了上面提到的情况一,开始回溯V1,V1无未被遍历的邻接点,接着回溯V0,V0有一个未被遍历的邻接点V2,新的一轮深度遍历从V2开始。V2无邻接点,且无法回溯。此时出现了情况二,检测visited[i],只有V4了。深度遍历完成。看到回溯,应该可以想到需要使用栈。
    遍历序列是
    V0->V1->V3->V2->V4。
    从其它顶点出发的深度优先遍历序列是:
    V1->V3->V0->V2->V4。
    V2->V0->V1->V3->V4。
    V3->V0->V1->V2->V4。
    V4->V2->V0->V1->V3。

     深度优先算法:

    template <int max_size>
    class Graph 
    {
        private:
            int count;
            bool adjacent[max_size][max_size]; //邻接矩阵中顶点用数组下标表示
        public:
            void dfs(int,void(*visit)(int)); //深度优先
            void bfs(int,void(*visit)(int)); //广度优先
    };
    
    
    void Graph::dfs(int vertex,void(*visit)(int)) 
    {
        stack<int> s; //用于存放遍历的顶点,回溯用
        bool visited[max_size]; //存放访问
        int count = 0; //用来统计已经访问过的结点的数目
        for(int i=0;i<max_size;i++)
            visited[i] = false;
        
        while(count < max_size) //当访问顶点数目小于总数时候一直循环
        {
            //先访问一个顶点
            visit(vertex);
            visited[vertex] = true; 
            s.push(vertex);
            count++;     
        
            if(count == max_size) break;
            
            while(visited[vertex])
            {
                for(int i=0;i < max_size && (visited[i] || adjacent[vertex][i] == 0);i++); //循环的作用是找到所有未被访问过的邻接结点
                if(i == max_size) //当前所有的可能的邻接结点都已经被访问了
                {
                    if(!s.empty())  //栈非空,得进行回溯到上一个结点
                    {
                        s.pop();
                        if(!s.empty())//弹出上一个结点后栈非空得取出栈中的上一个元素
                        {
                            vertex = s.top();
                            s.pop();
                        }
                        else
                        {
                            //栈空了得从头寻找没有访问过的结点
                            for(vertex=0;vertex<max_size && visited[vertex];i++); 
                        }
                        
                    }
                    else
                    {
                        //栈空了得从头寻找没有访问过的结点
                        for(vertex=0;vertex < max_size && visited[vertex];i++);
                    }
                    
                }
                //当前结点的邻接结点没有被访问完时,继续访问邻接结点。
                else 
                    vertex = i; 
            }
        }
        
        delete [] visited; //回收内存
    }

    广度优先遍历

        广度优先遍历也叫广度优先搜索(Breadth First Search)。
    它的遍历规则:
    1. 先访问完当前顶点的所有邻接点。(应该看得出广度的意思)
    2. 先访问顶点的邻接点先于后访问顶点的邻接点被访问。
        具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,且顶点V0的邻接点下表从小到大有Vi、Vj...Vk。按规则1,接着应遍历Vi、Vj和Vk。再按规则2,接下来应遍历Vi的所有邻接点,之后是Vj的所有邻接点,...,最后是Vk的所有邻接点。接下来就是递归的过程...
    在广度遍历的过程中,会出现图不连通的情况,此时也需按上述情况二来进行:测试visited[i]...。在上述过程中,可以看出需要用到队列。
     
    例如:
    从V0开始遍历
        遍历分析:V0有两个邻接点V1和V2,于是按序遍历V1、V2。V1先于V2被访问,于是V1的邻接点应先于V2的邻接点被访问,那就是接着访问V3。V2无邻接点,只能看V3的邻接点了,而V0已被访问过了。此时需检测visited[i],只有V4了。广度遍历完毕。
    遍历序列是
    V0->V1->V2->V3->V4。
    从其它顶点出发的广度优先遍历序列是
    V1->V3->V0->V2->V4。
    V2->V0->V1->V3->V4。
    V3->V0->V1->V2->V4。
    V4->V2->V0->V1->V3。

    广度优先算法:

    template <int max_size>
    class Graph 
    {
        private:
            int count;
            bool adjacent[max_size][max_size]; //邻接矩阵中顶点用数组下标表示
        public:
            void dfs(int,void(*visit)(int)); //深度优先
            void bfs(int,void(*visit)(int)); //广度优先
    };
    
    
    void Graph::bfs(int vertex,void(*visit)(int)) 
    {
        queue<int> q; //用对列来存放访问过的结点
        bool visited[max_size];
        for(int i=0;i<max_size;i++) 
            visited[i] = false;
        int count = 0;  //用于统计遍历过的结点的数目
        q.push(vertex);
        visit(vertex);
        visited[vertex] = true;
        count++;
        while(count < max_size)
        {
            //访问完vertex后从queue中取出vertex结点。判断queue是否为空,再进行相应的操作
            if(!q.empty())
            {
                vertex = q.front();
                q.pop();
            }
            else
            {
                for(vertex=0;vertex<max_size && visited[vertex];vertex++);
                visit(vertex);
                visited[vertex] = true;
                count++;
                if(count == max_szie) return;
                q.push(vertex);
            }
            //访问完结点vertex,于是下面开始访问vertex的所有邻接结点
            for(int i=0;i<max_size;i++)
            {
                if(!visited[i] && adjacent[vertex][i] != 0)
                {
                    visit(i);
                    visited[i] = true;
                    count++;
                    if(count == max_size) return;
                    q.push(i);
                }
            }
        }
        delete [] visited; //回收内存
    }

    图的深度优先遍历参考:

    【1】http://blog.csdn.net/zhangxiangdavaid/article/details/38321327

    【2】http://blog.csdn.net/zhangxiangdavaid/article/details/38323633

  • 相关阅读:
    hibernate3.2多表关联查询常见问题
    Map 四种同步方式的性能比较
    架构师书单(2010版)
    强碱性食品 高嘌呤食物
    Linux内核crash/Oops异常定位分析方法
    linux驱动基础系列linux spi驱动框架分析
    vmware server 虚拟机与宿主机之间共享网络设置问题
    花生壳
    Groove 线上办公室
    coolit
  • 原文地址:https://www.cnblogs.com/jeavenwong/p/8204812.html
Copyright © 2011-2022 走看看