基本图算法之图的搜索

介绍完图的存储结构以及如何从外部读取txt文件来创建一个图的内容,我们开始介绍关于图的入门的一个算法——图的搜索,也就是我们常说的图的遍历,与(二叉)树的遍历方式(先序遍历、中序遍历、后序遍历、层次遍历)类似,首先图的遍历有两种方式 广度优先遍历(BFS)、深度优先遍历(DFS)。  关于基本的[DFS](https://en.wikipedia.org/wiki/Depth-first_search)和[BFS](https://en.wikipedia.org/wiki/Breadth-first_search)的思路不再进行介绍,这里主要记载我在编写时遇到的问题。  在我刚刚看完关于图的两种搜索(遍历)算法时,说这两种方法的时间复杂度均为O(v+e),也就相信了两种算法在实现后跑相同的图集的时候,耗时应该是相差不多的,这种差距当然在跑一些只有一万顶点、一万边的图中不会看出来,因为耗时都很短,但是当我使用大的数据集进行测试的时候发现两种算法之间的时间使用还有不小的差距的,下面通过我前期写和代码来分析,为什么会有这样的情况发生。  我们先聊下DFS,DFS的实现方式我们分为两种,递归式和非递归式,递归则是比较简单,也很容易实现,如下:
 void Graph::DFS_Sin(int v){
    Edge* e;
    head[v].statu=true;
    e=head[v].next;
    while(e!=NULL){
        if (!head[e->vName].statu)
        {
            DFS_Sin(e->vName);
        }
        e=e->nextEdge;
    }
}
 理论上使用递归的方式速度要慢于非递归的方式的,但是当我写完成之后发现非如此,非但不比递归的方式要慢,还慢的出奇。。。。。很明显是出现了错误,下面贴出代码:
void Graph::DFS(){  
    stack <int> st;  
    for(int i=0;i<vex_num;++i){  
        if (head[i].statu==false)  
        {  
            st.push(i);
            head[i].statu=true;
            Edge* temp;
            while(!st.empty()){
                temp=head[st.top()].next;/*这种写法的时间消耗必定要长;
                主要是就因为我们从栈中出来之后都是从当前顶点撒向的第一条边开始扫描的,
                也就是说每条边我们都要访问多次,不仅仅是一次,
                所以在实际跑程序的时候消耗的时间要长的多久*/
                while(temp!=NULL && head[temp->vName].statu==true)
                    temp=temp->nextEdge;
                if (temp==NULL)
                {
                    st.pop();
                }else{
                    head[temp->vName].statu=true;
                    st.push(temp->vName);
                }
            }//while;
        }//if;
    }
}
 以上标出了问题出现的地方,也就是说,如果使用递归的形势进行深度搜索时,我们每个边只搜索了一次,但是使用我那样的 错误方法写时,对每条边的搜索次数将大大大的增加,所以对于处理大的图的时候,这种写法总是不能在可接受时间内完成图的完整遍历。那我们应该如何写出DFS的非递归方式,其实也是在
  procedure DFS-iterative(G,v):
      let S be a stack
      S.push(v)
      while S is not empty
            v = S.pop()
            if v is not labeled as discovered:
                label v as discovered
                for all edges from v to w in G.adjacentEdges(v) do
                    S.push(w)
 以上算法是如何解决我的那个问题的呢?我是将顶点入栈的同时进行访问标记,而正确的写法则采用了出栈的时候才进行标记。同时,与递归的方法相比,此种非递归的方式是先访问与顶点相连的最后一条边,而递归的方式则是先访问第一条边,下面引用原文,出自 wikipedia: >These two variations(注:指的是递归方式和非递归方式) of DFS visit the neighbors of each vertex in the opposite order from each other: the first neighbor of v visited by the recursive variation is the first one in the list of adjacent edges, while in the iterative variation the first visited neighbor is the last one in the list of adjacent edges. The recursive implementation will visit the nodes from the example graph in the following order: A, B, D, F, E, C, G. The non-recursive implementation will visit the nodes as: A, E, F, B, D, C, G. The non-recursive implementation is similar to breadth-first search but differs from it in two ways: it uses a stack instead of a queue, and it delays checking whether a vertex has been discovered until the vertex is popped from the stack rather than making this check before pushing the vertex. Note that this non-recursive implementation of DFS may use O(|E|) space in the worst case, for example on a complete graph.  BFS的方法也较为简单,也没有遇到什么疑问,所以不在占用篇幅来贴写代码,后期会将全部代码上传至[github](https://github.com/tianzhuwei)。  知道两种方法的代码实现之后来分析,为什么两者(DFS,BFS)两种方式的时间差距会这么大( 测试的图为无向图),如下图1:

按照正常来说,时间不可能相差太多,下面进行下代码的排查!!!
 写到这里不知道是不是自己代码存在的错误呢?因为数据集比较大,不可以一一测试,所以接下来找同学一起来测试一下相同的数据,看看时间消耗如何,测试结果后续更新!今天暂且到这里^_^


更新

特意测试仪了一下BFS中使用自己定义的队列与使用STL中queue 的速度使用的图为有向图和无向图,下面上图:

会发现自己定义的队列确实要比使用STL中的queue要快,之后再将DFS中栈进行自定义进行比较,看是否有同样的影响。

下面又测试了一下DFS使用递归方式和非递归方式的时间比较,上图:

很淡疼的在处理有向图数据时非递归的方式居然比递归的方式还要慢,问题出在哪里??
稍后会再列出使用自己定义的栈和调用STL中的栈实现DFS时的时间比较图:

最后得出的结论应该不是我的代码存在错误,DFS的搜索速度确实要快于BFS,但是占用的空间也要大,基本上符合“用空间换取时间的操作”下面再上一张最后DFS和BFS比较:


这里找到一个比较好STL容器与自定义数组的帖子,上一个关键截图

原文出自,详情点击http://www.cppblog.com/sailing/articles/161659.html查看。

额额这个行文的思路很乱,因为完全是遇到什么问题再云解决,以后注意!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值