代码改变世界

图的DFS递归和非递归

  youxin  阅读(3753)  评论(1编辑  收藏  举报

看以前写的文章:

图的BFS:http://www.cnblogs.com/youxin/p/3284016.html

DFS:http://www.cnblogs.com/youxin/archive/2012/07/28/2613362.html

递归:

参考了算法导论

复制代码
int parent[50];
int
color[50];//0代表white,1 gray 2 black static int time=0; int d[50];//顶点v第一次被发现(并置v为gray色) int f[50];//结束检测v的邻接表(并置v为黑色) void DFS(Graph G,int u); void DFSTraverse(Graph G,void (*Visit)(VextexType v)) { visitFunc=Visit; for(int i=0;i<G.vexnum;i++) { color[i]=0; parent[i]=-1; } time=0; for(int i=0;i<G.vexnum;i++) { if(color[i]==0) //未访问 DFS(G,i); } } void DFS(Graph G,int u) { color[u]=1;//white vextex has just been discovered visitFunc(G.vertices[u].data); time=time+1; d[u]=time; ArcNode *p=G.vertices[u].firstarc; while(p) { int v=p->adjvex; if(color[v]==0) { parent[v]=u; DFS(G,v); } p=p->nextarc; } color[u]=2;//black,it's finished f[u]=time=time+1; }
复制代码
   DFSTraverse(G,visit);
     cout<<endl;
         
     for(int i=0;i<G.vexnum;i++)
         cout<<i<<ends<<parent[i]<<endl;
     cout<<endl;
              
     for(int i=0;i<G.vexnum;i++)
         cout<<i<<ends<<d[i]<<"----"<<f[i]<<endl;
复制代码
复制代码

可以看到DFS输出:

v1 v3 v7 v6 v2 v5 v8 v4。

 

非递归要用到栈。

复制代码
void DFS2(Graph G,int u)
{
     
    stack<int> s;
    visited[u]=true;
    s.push(u);
    
    while(!s.empty())
    {
        int v=s.top();s.pop();
         
        
        visitFunc(G.vertices[v].data);

        ArcNode *p=G.vertices[v].firstarc;
        while(p)
        {
            if(!visited[p->adjvex])
            {
                s.push(p->adjvex);
                visited[p->adjvex]=true;
                
            }
            p=p->nextarc;
        }
        
    }
}
复制代码

写非递归时千万要记住的是,进栈时一定要设置visited[i]=true(包括原顶点);不然有些节点会重复进栈DFS和

二叉树的先序遍历是一样的,只不过二叉树只有2个分支,要要进右分支,在进左分支,而图只要是邻接点都进去,不分先后

 

下面的代码写的比较烂:(不要用,用上面的)

复制代码
void DFS2(Graph G,int u)
{
    color[u]=1;//white vextex has just been discovered
    visitFunc(G.vertices[u].data);
    time=time+1;
    d[u]=time;
    stack<int> s;
    ArcNode *p=G.vertices[u].firstarc;
    while(p)
    {
        color[p->adjvex]=1;
        s.push(p->adjvex);
        p=p->nextarc;
    }
    while(!s.empty())
    {
        int v=s.top();s.pop();
        //color[v]=1;//white vextex has just been discovered,这句话可以不要,因为在进栈时已经设置了
        visitFunc(G.vertices[v].data);

        ArcNode *p2=G.vertices[v].firstarc;
        while(p2)
        {
            if(color[p2->adjvex]==0)
            {
                s.push(p2->adjvex);
                color[p2->adjvex]=1;//每次进栈都要设置1
                 
            }
            p2=p2->nextarc;
        }
         
    }
}
复制代码

这里的d[ ]和f【】不好写。

输出:

v1 v2 v4v8 v5 v3 v6 v7

 

邻接矩阵的非递归代码:

复制代码
#include
#include
#define max_node 20
#define max 20000
using namespace std;

int map[max_node][max_node];

void dfs(int start,int n)
{
    stack s;
    int i,vis[max_node],ctop;
    memset(vis,0,sizeof(vis));
    vis[start] = 1;
    printf("%d ",start);
    for (i = 1;i <= n;i++)
        if(!vis[i] && map[i][start] == 1)
        {
            vis[i] = 1;
            s.push(i);
        }
    while(!s.empty())
    {
        ctop = s.top();
        vis[ctop] = 1;
        printf("%d ",s.top());
        s.pop();
        for (i = 1;i <= n;i++)
            if(!vis[i] && map[i][ctop] == 1)
            {
                vis[i] = 1;
                s.push(i);
            }
    }
}

int main()
{
    int s,t,n;
    scanf("%d",&n);
    memset(map,max,sizeof(map));
    while(1)
    {
        scanf("%d %d",&s,&t);
        if(s == 0) break;
        map[s][t] = map[t][s] = 1;
    }
    dfs(1,n);
    return 0;
}
输入:

8
1 2
1 3
2 4
2 5
4 8
5 8
3 6
3 7
0 0

输出:
1 3 7 6 2 5 8 4
View Code
复制代码

图的深度优先算法的递归版本相当简洁好懂。将递归版本的算法改写成非递归版本的难度并不大,关键是要处理好如何正确的在搜索的过程中存储搜索树中的子结点,并正确的进行访问.一种实现采用了两个栈,而另一种则使用一个结点类型为队列的栈..

 

 

对于下面这段html代码,要求打印出每个节点的标签名和类名:

 <div id='root'>
        <span>123
            <a href="#">
                sdsd
            </a>
            <div>sdsd<a>这是一个a标签</a></div>
        </span>
        <span>456
            <p>这是一个p标签</p>
        </span>
    </div>


面给出该算法的js实现:

(1)这是深度优先算法的递归实现

function deepTraversal(node,nodeList) {  
    if (node) {    
            nodeList.push(node);    
            var children = node.children;    
            for (var i = 0; i < children.length; i++) 
      //每次递归的时候将  需要遍历的节点  和 节点所存储的数组传下去
                deepTraversal(children[i],nodeList);    
        }    
    return nodeList;  
}  
var root = document.getElementById('root')
console.log(deepTraversal(root,nodeList=[]))

(2)这是深度优先算法的递归实现

function deepTraversal(node) {  
    var nodeList = [];  
    if (node) {  
        var stack = [];  
        stack.push(node);  
        while (stack.length != 0) {  
            var childrenItem = stack.pop();  
            nodeList.push(childrenItem);  
            var childrenList = childrenItem.children;  
            for (var i = childrenList.length - 1; i >= 0; i--)  
                stack.push(childrenList[i]);  
        }  
    }    
    return nodeList;  
}   
var root = document.getElementById('root')
console.log(deepTraversal(root))

广度优先遍历

当使用广度优先遍历的时候,先依次遍历兄弟节点,然后便利兄弟节点下的子节点
结果一般为:


 
广度优先的结果.png

广度优先遍历二叉树,也就是按层次的去遍历。依次遍历根节点,然后是左孩子和右孩子。所以要遍历完当前节点的所有孩子,。根据左右孩子的顺序来输出,所以就是先进先出的原则,那么我们当然就想到了队列这个数据结构:

 
广度优先遍历.png

(1) 广度优先算法的的非递归实现

function wideTraversal(node) {  
    var nodes = [];  
    if (node != null) {  
        var queue = [];  
        queue.unshift(node);  
        while (queue.length != 0) {  
            var item = queue.shift();  
            nodes.push(item);  
            var children = item.children;  
            for (var i = 0; i < children.length; i++)  
                queue.push(children[i]);  
        }  
    }  
    return nodes;  
}
var root = document.getElementById('root');
console.log(wideTraversal(root));
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2012-08-27 javascript 事件处理
2012-08-27 Javascript DOM 编程艺术:ENHANCING CONTENT
点击右上角即可分享
微信分享提示