图的深度优先遍历和广度优先遍历 Java实现

一 图的基本概念及存储结构
图G是由顶点的有穷集合,以及顶点之间的关系组成,顶点的集合记为V,顶点之间的关系构成边的集合E
G=(V,E).
说一条边从v1,连接到v2,那么有v1Ev2(E是V上的一个关系)《=》<v1,v2>∈E.
图有有向图,无向图之分,无向图的一条边相当于有向图的中两条边,即如果无向图的顶点v1和v2之间有一条边
,那么既有从v1连接到v2的边,也有从v2连接到v1的边,<v1,v2>∈E 并且<v2,v1>∈E,而有向图是严格区分方向的。

一般图的存储有两种方式
1)相邻矩阵,用一个矩阵来保持边的情况,<v1,v2>∈E则Matrix[v1][v2]=Weight.
2)邻接表,需要保存一个顺序存储的顶点表和每个顶点上的边的链接表。
这里的实现采用第二种方案,另外图又复杂图,简单图之分,复杂图可能在两点之间同一个方向有多条边,我们考虑的都是无环简单图,无环简单图是指顶点没有自己指向自己的边的简单图,即任取vi属于V => <vi,vi>不属于E并且没有重复边。

我们先给出图的ADT:

package
 algorithms.graph;


/**

 * The Graph ADT
 * 

@author
 yovn
 *
 

*/


public
 
interface
 Graph {
    
    

//
mark


    
public
 
static
 
interface
 Edge{
        

public
 
int
 getWeight();
    }
    
    

int
 vertexesNum();
    
    

int
 edgeNum();
    

boolean
 isEdge(Edge edge);
    

void
 setEdge(
int
 from,
int
 to, 
int
 weight);
    Edge firstEdge(

int
 vertex);
    Edge nextEdge(Edge edge);
    

int
 toVertex(Edge edge);
    

int
 fromVertex(Edge edge);
    String getVertexLabel(

int
 vertex);
    

void
 assignLabels(String[] labels);
    

void
 deepFirstTravel(GraphVisitor visitor);
    

void
 breathFirstTravel(GraphVisitor visitor);
    
    

}

其中的方法大多数比较一目了然,
其中
1)Edge firstEdge(int vertex)返回指定节点的边的链表里存的第一条边
2)
Edge nextEdge(Edge edge),顺着边链表返回下一条边
3)fromVertex,toVertex很简单返回该边的起始顶点和终结顶点
4)
getVertexLabel返回该定点对应的标号,assignLabels给所有顶点标上号

GraphVisitor是一个很简单的接口:

package
 algorithms.graph;


/**

 * 

@author
 yovn
 *
 

*/


public
 
interface
 GraphVisitor {
    
    

void
 visit(Graph g,
int
 vertex);

}

OK,下面是该部分实现:

package
 algorithms.graph;


import
 java.util.Arrays;


/**

 * 

@author
 yovn
 *
 

*/


public
 
class
 DefaultGraph 
implements
 Graph {
    

    
private
 
static
 
class
 _Edge 
implements
 Edge{
        
        
        

private
 
static
 
final
 _Edge NullEdge
=
new
 _Edge();
        
        

int
 from;
        

int
 to;
        

int
 weight;
        
        _Edge nextEdge;
        
        

private
 _Edge()
        {
            weight

=
Integer.MAX_VALUE;
        }
        
        _Edge(

int
 from, 
int
 to, 
int
 weight)
        {
            
            

this
.from
=
from;
            

this
.to
=
to;
            

this
.weight
=
weight;
        }
        

public
 
int
 getWeight()
        {
            

return
 weight;
        }
        
        
    }
    
    

private
 
static
 
class
 _EdgeStaticQueue
    {
        _Edge first;
        _Edge last;
    }
    
    

private
 
int
 numVertexes;
    

private
 String[] labels;
    

private
 
int
 numEdges;
    
    
    

private
 _EdgeStaticQueue[] edgeQueues;
    
    

//
tag the specified vertex be visited or not


    
private
 
boolean
[] visitTags;

    
/**

     * 
     

*/

    

public
 DefaultGraph(
int
 numVertexes) {
        

if
(numVertexes
<
1
)
        {
            

throw
 
new
 IllegalArgumentException();
        }
        

this
.numVertexes
=
numVertexes;
        

this
.visitTags
=
new
 
boolean
[numVertexes];
        

this
.labels
=
new
 String[numVertexes];
        

for
(
int
 i
=
0
;i
<
numVertexes;i
++
)
        {
            labels[i]

=
i
+
“”
;
            
            
        }
        

this
.edgeQueues
=
new
 _EdgeStaticQueue[numVertexes];
        

for
(
int
 i
=
0
;i
<
numVertexes;i
++
)
        {
            edgeQueues[i]

=
new
 _EdgeStaticQueue();
            edgeQueues[i].first

=
edgeQueues[i].last
=
_Edge.NullEdge;
            
        }
        

this
.numEdges
=
0
;
    }

    

    
/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#edgeNum()
     

*/

    @Override
    

public
 
int
 edgeNum() {
        
        

return
 numEdges;
    }

    
/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#firstEdge(int)
     

*/

    @Override
    

public
 Edge firstEdge(
int
 vertex) {
        

if
(vertex
>=
numVertexes)    
throw
 
new
 IllegalArgumentException();
        

return
 edgeQueues[vertex].first;
        
    }

    
/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#isEdge(algorithms.graph.Graph.Edge)
     

*/

    @Override
    

public
 
boolean
 isEdge(Edge edge) {
        
        

return
 (edge
!=
_Edge.NullEdge);
    }

    
/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#nextEdge(algorithms.graph.Graph.Edge)
     

*/

    @Override
    

public
 Edge nextEdge(Edge edge) {
        
        

return
 ((_Edge)edge).nextEdge;
    }

    
/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#vertexesNum()
     

*/

    @Override
    

public
 
int
 vertexesNum() {
        
        

return
 numVertexes;
    }

    @Override
    
public
 
int
 fromVertex(Edge edge) {
        
        

return
 ((_Edge)edge).from;
    }

    @Override
    
public
 
void
 setEdge(
int
 from, 
int
 to, 
int
 weight) {
        

//
we don’t allow ring exist 


        
if
(from
<
0
||
from
>=
numVertexes
||
to
<
0
||
to
>=
numVertexes
||
weight
<
0
||
from
==
to)
throw
 
new
 IllegalArgumentException();
        _Edge edge

=
new
 _Edge(from,to,weight);
        edge.nextEdge

=
_Edge.NullEdge;
        

if
(edgeQueues[from].first
==
_Edge.NullEdge)
            edgeQueues[from].first

=
edge;
        

else
 
            edgeQueues[from].last.nextEdge

=
edge;
        edgeQueues[from].last

=
edge;
        
    }

    @Override
    
public
 
int
 toVertex(Edge edge) {

        
return
 ((_Edge)edge).to;
    }

    @Override
    
public
 String getVertexLabel(
int
 vertex) {
        

return
 labels[vertex];
    }

    @Override
    
public
 
void
 assignLabels(String[] labels) {
        System.arraycopy(labels, 

0

this
.labels, 
0
, labels.length);
        
    }

       
//
to be continue《图的深度优先遍历和广度优先遍历 Java实现》



}

二 深度优先周游
即从从某一点开始能继续往前就往前不能则回退到某一个还有边没访问的顶点,沿这条边看该边指向的点是否已访问,如果没有访问,那么从该指向的点继续操作。

那么什么时候结束呢,这里我们在图的ADT实现里加上一个标志数组。该数组记录某一顶点是否已访问,如果找不到不到能继续往前访问的未访问点,则结束。

你可能会问,如果指定图不是连通图(既有2个以上的连通分量)呢?
OK,解决这个问题,我们可以让每一个顶点都有机会从它开始周游。
下面看deepFirstTravel的实现:

 
/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#deepFirstTravel(algorithms.graph.GraphVisitor)
     

*/

    @Override
    

public
 
void
 deepFirstTravel(GraphVisitor visitor) {
        Arrays.fill(visitTags, 

false
);
//
reset all visit tags


        
for
(
int
 i
=
0
;i
<
numVertexes;i
++
)
        {
            

if
(
!
visitTags[i])do_DFS(i,visitor);
        }

    }
    

    
private
 
final
 
void
 do_DFS(
int
 v, GraphVisitor visitor) {
        

//
first visit this vertex


        visitor.visit(
this
, v);
        visitTags[v]

=
true
;
        
        

//
for each edge from this vertex, we do one time
        

//
and this for loop is very classical in all graph algorithms


        
for
(Edge e
=
firstEdge(v);isEdge(e);e
=
nextEdge(e))
        {
            

if
(
!
visitTags[toVertex(e)])
            {
                do_DFS(toVertex(e),visitor);
            }
        }
        
        
    }

三 广度优先周游
广度优先周游从每个指定顶点开始,自顶向下一层一层的访问。上一层所有顶点访问完了才继续下一层的访问。可以把二叉树的广度优先周游看成图的广度优先周游的特例。
(二叉树是连通的无回路的有向图,也是一棵根树)
同样,广度优先也要借助与一个队列来存储待访问顶点

下面是breathFirstTravel的实现,为了减小Java库的影响,我自己实现一个很简单的队列。(你也可以使用
ArrayList,但是记住队列的定义,只能在头删除,在尾插入):

private
 
static
 
class
 _IntQueue
    {
        

private
 
static
 
class
 _IntQueueNode
        {
            _IntQueueNode next;
            

int
 value;
        }
        _IntQueueNode first;
        _IntQueueNode last;
        
        

//
queue can only insert to the tail


        
void
 add(
int
 i)
        {
            _IntQueueNode node

=
new
 _IntQueueNode();
            node.value

=
i;
            node.next

=
null
;
            

if
(first
==
null
)first
=
node;
            

else
 last.next
=
node;
            last

=
node;
        }
        
        
        

boolean
 isEmpty()
        {
            

return
 first
==
null
;
        }
        

//
queue can only remove from the head


        
int
 remove()
        {
            

int
 val
=
first.value;
            

if
(first
==
last)
                first

=
last
=
null
;
            

else

                first

=
first.next;
            

return
 val;
        }
        
    }
    

/*
 (non-Javadoc)
     * @see algorithms.graph.Graph#breathFirstTravel(algorithms.graph.GraphVisitor)
     

*/

    @Override
    

public
 
void
 breathFirstTravel(GraphVisitor visitor) {
        Arrays.fill(visitTags, 

false
);
//
reset all visit tags


        
        
        

for
(
int
 i
=
0
;i
<
numVertexes;i
++
)
        {
            

if
(
!
visitTags[i])
            {
                    
                do_BFS(i,visitor);
            }
        }

    }

    
private
 
void
 do_BFS(
int
 v, GraphVisitor visitor) {
        

//
and BFS will use an queue to keep the unvisited vertexes
        

//
 we  can also just use java.util.ArrayList


        _IntQueue queue
=
new
 _IntQueue();
        queue.add(v);
        

while
(
!
queue.isEmpty())
        {
            

int
 fromV
=
queue.remove();
            visitor.visit(

this
, fromV);
            visitTags[fromV]

=
true
;
            

for
(Edge e
=
firstEdge(fromV);isEdge(e);e
=
nextEdge(e))
            {
                

if
(
!
visitTags[toVertex(e)])
                {
                    queue.add(toVertex(e));
                }
            }
        }
    }

OK,最后我们测试一下:

/**

     * 

@param
 args
     

*/

    

public
 
static
 
void
 main(String[] args) {
        

/**

         * For example, we have a graph:
         * 0→1→2
         * ↓ ↑↓
         * 3  4 5
         * ↓ ↑↓
         * 6→7←8
         * 
         *  here ,all weight is 0, its a DAG(Directed Acyclic Graph) 
         

*/

        DefaultGraph g
=
new
 DefaultGraph(
9
);
        g.setEdge(

0

1

0
);
        g.setEdge(

0

3

0
);
        g.setEdge(

1

2

0
);
        g.setEdge(

4

1

0
);
        g.setEdge(

2

5

0
);
        
        g.setEdge(

3

6

0
);
        g.setEdge(

7

4

0
);
        g.setEdge(

5

8

0
);
        g.setEdge(

6

7

0
);
        g.setEdge(

8

7

0
);
        
        

//
now we visit it


        GraphVisitor visitor
=
new
 GraphVisitor()
        {
            @Override
            

public
 
void
 visit(Graph g, 
int
 vertex) {
                System.out.print(g.getVertexLabel(vertex)

+

 

);
                
            }
            
        };
        System.out.println(


DFS==============:

);
        g.deepFirstTravel(visitor);
        System.out.println();
        System.out.println(


BFS==============:

);
        g.breathFirstTravel(visitor);
        System.out.println();
    }

 

 

转自http://www.blogjava.net/javacap/archive/2007/12/14/167764.html

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/cocolee100/article/details/5024216
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞