演算法(四)——圖

Kocurry發表於2020-11-30

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之間。

相關文章