算法(四)——图

4.1  图

关于图,包括:无向图(简单连接)、有向图(连接有方向性)、加权图(连接带有权值)和加权有向图(连接既有方向性又带有有权值)。

无向图

定义:图是由一组顶点和一组能够将两个顶点相连的边组成的。

  • 自环:即一条连接一个顶点和其自身的边;
  • 连接同一对顶点的两条边称为平行边。

定义:在图中,路径是由边顺序连接的一系列顶点。简单路径是一条没有重复顶点的路径。环是一条至少含有一条边且起点和终点相同的路径。简单环是一条(除了起点和终点必须相同之外)不含有重复顶点和边的环。路径或者环的长度为其中所包含的边数。

定义:如果从任意一个顶点都存在一条路径到达另一个任意顶点,我们称这幅图是连通图。一幅非连通的图由若干连通的部分组成,它们都是其极大连通子图。

定义:树是一幅无环连通图。互不相连的树组成的集合称为森林。连通图的生成树是它的一幅子图,它含有图中的所有顶点且是一棵树。图的生成树森林是它的所有连通子树的生成树的集合。

4.1.1  表示无向图的数据类型

要开发处理图问题的各种算法,我们首先来看一份定义了图的基本操作的API。

                                                                                           表4.1.1  无向图的API

public class Graph 
Graph(int V)创建一个含有V个顶点但不含有边的图
Graph(In in)从标准输入流in读入一幅图
int V()顶点数
int E()边数
void addEdge(int v, int w)向图中添加一条边v-w
Iterable<Integer> adj(int v)和v相邻的所有顶点
String toString()对象的字符串表示

                                                                                          表4.1.2  最常用的图处理代码

任务实现
计算v的度数

public static int degree(Graph G,int v)

{

     int degree = 0;

     for( int w : G.adj(v) )

     degree++;

     return degree;

}

计算所有顶点的最大度数

public static int maxDegree(Graph G)

{

   int max = 0;

   for( int v = 0; v<G.V();v++)

     if( degree(G,v) >max)

         max = degree(G,v);

   return max;

}

计算所有顶点的平均度数

public static double avgDegree( Graph G )

{    return 2.0*G.E( ) / G.v( );  }

计算自环的个数

public static int numberOfSelfLoops( Graph G)

{

   int count = 0;

   for( int v = 0;v<G.V();v++)

        for(int w:G.adj(v))

          if(v==w) count++;

   return count/2; //每条边都被记过两次

}

图的邻接表的字符串表示(Graph的实例方法)

public String toString()

{

    String s = V +"  vertices, " + E + " edges\n ";

    for( int v = 0 ;v<V ; v++ )

    {

       s += v +" : ";

       for( int w :this.adj(v) )

        s += w+" ";

       s += "\n";

    }

    return s;

}

Graph数据类型

public class Graph
{
    private final int V;               //顶点数目
    private int E;                     //边的数目
    private Bag<Integer>[] adj;        //邻接表
    public Graph(int V)
    {
      this.V = V; this.E = 0;
      adj = ( Bag<Integer>[]) new Bag[V];   //创建邻接表
      for(int v = 0; v < V; v++)            //将所有链表初始化为空
          adj[v] = new Bag<Integer>();
    }

    public Graph(In in)
    {
      this(in.readInt());                //读取V并将图初始化
      int E = in.readInt();              //读取E
      for(int i = 0; i < E; i++) 
         {    
           int v = in.readInt();         //读取一个顶点
           int w = in.readInt();         //读取另一个顶点
           addEdge(v,w);                 //添加一条连接它们的边
         }
    }
    public int V() { return V;}
    public int E() { return E;}
    public void addEdge( int v , int w )
    {
      adj[v].add(w);                    //将w添加到v的链表中
      adj[w].add(v);                    //将v添加到w的链表中
    E++;
    }
    public Iterable<Integer> adj(int v)
    { return adj[v]; }
}

这份Graph的实现使用了一个由顶点索引的整型链表数组。每条边都会出现两次,即当存在一条连接v与w的边时,w会出现在v的链表中,v也会出现在w的链表中。第二个构造函数从输入流中读取一幅图,开头是V,然后是E,再然后是一列整数对,大小在0到V-1之间。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页