浅谈图的深层优先和广度优先遍历(栈实现,循环内层嵌套递归实现)(c++)

最近刚好学到了图的深层优先遍历(DFS),发现了一些误区,比如“Skiena's Algorithm Design Manual 第 169页; Jeff Edmonds' How to Think about Algorithms, 第 175–178页; Gilberg and Forouzan Data Structures: A Pseudocode Approach Using C, 第 497页。”

包括维基百科中也出现了同样的错误,也就是直接将广度优先遍历(BFS)中的queue直接替换成了stack,显然会出现一些问题。

https://11011110.github.io/blog/2013/12/17/stack-based-graph-traversal.html

这个网站中指出了这些问题。

 

 简单概括:把queue直接替换成stack会先访问一些(如果使用DFS)应该在后面访问的节点。

BFS(s)
{for( each vertex v){
    flag(v) = false}//flag用于标记已经访问过的顶点。先全部标记为false
    queue q;
    q.push(s);
    flag(s) = true;//访问过了源定点,标记为true
    while{!q.empty()){
        v = q.front();
        q.pop();
         for(顶点v连接的顶点w){
           if(flag(v) = false){//如果没有访问
            flag(v) = true;//访问过了
            q.push(v);//压入队列中}
}
}

        

这是一种使用queue来广度优先(BFS)的思路

也就是说:

1、访问列表里所有的顶点为false(未访问)

2、创建queue

3、将源顶点压入queue里

4、弹出queue里的一个顶点(遍历实际在这里进行)

5、把这个顶点里的所有没有访问过的邻居(有线连着的)依次压入栈里

6、重复4-5直到栈为空

例如:

 BFS遍历顺序:2->8->1->4->0->9->3->7->5->6

时间复杂程度:O(顶点+边数)

接下来是广度优先遍历(DFS)

1、和BFS一样,先把访问列表全部设成false
2、DFS(v){
flag[v] = true;
for(v的每一个邻居w){
if(flag[w] = false){
DFS(w);}}

这里代码的难点在

DFS(v)

for(v的每一个邻居w){
if(flag[w] = false){
DFS(w);}

}

这是一个循环嵌套递归

在编译器中大概是这样的

1、进入函数调用时循环被“暂停”。

2、新的递归调用开始for并再次循环,在再次调用函数时暂停,依此类推。

 2(pause)->8(pause)->0

                        8(continiue)->9(pause)->1(pause)->3(pause)->4

                                                                                          3->5(pause)->6(pause)->7

6(continue):所有的邻居(7、5)都访问过了(使用visited list,不满足if(flag[w] == false)里面的东西不再执行.

5(continue):3,6访问过了,if语句不再执行。

以此类推 直到2

遍历结果:2 8 0 9 1 3 4 5  6 7

循环嵌套递归也可以理解为

for (  )
{
    for ( )
    {
        for (  )
        {
        }
    }
}

很多个for嵌套在一起

 其实会发现DFS的操作似乎和栈(stack)有类似的地方:

使用栈来实现(仿递归函数)

1、创建stack

2、flag[源顶点] = true;

3、把当前顶点 v 压入栈里(遍历发正在这里)

4、根据顺序,把v的一个邻居压入栈中

5、如果栈顶 里的顶点 所有邻居都访问到了,弹出,没有访问到,继续压入,直到栈为空。

使用代码表示:

vector<int> visited(n,0);
        stack<int> s;
        visited[2] =1;
        s.push(2);
    cout << "2"<<endl;
       while(!s.empty()){
            int temp=s.top();
             int check = 0;
            for(int i = 0; i< graph[temp].size();i++){
                if(visited[graph[temp][i]] == 1){
                    check ++;
                }
            }
            if(check == graph[temp].size()){
                s.pop();
            }
           if(s.size() ==0){return;}
            int temp1 = s.top();
            for( int i = 0; i<graph[temp1].size();i++){
                if(visited[graph[temp1][i]] ==0){
                    s.push(graph[temp1][i]);
                    visited[graph[temp1][i]] =1;
                    cout << "遍历中"<<graph[temp1][i]<<endl;
                    break;
                }
            }
        }
}

代码应该还可以优化 

                                

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值