代码改变世界

图的DFS递归和非递归

2013-08-27 03:44  youxin  阅读(3801)  评论(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));