图及其算法复习(Java实现) 一:存储结构,深度优先周游,广度优先周游

原文地址:http://www.blogjava.net/javacap/archive/2007/12/14/167764.html

一 图的基本概念及存储结构
图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

}


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

那么什么时候结束呢,这里我们在图的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();
    }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值