【學習筆記----數據結構16-圖的存儲結構】

圖的存儲結構

圖的存儲結構相較線性表與樹來說就更加複雜了。首先,我們口頭上說的“頂點的位置”或“鄰接點的位置”只是一個相對的概念。其實從圖的邏輯結構定義來看,圖上任何一個頂點都可被看成是第一個頂點,任一頂點的鄰接點之間也不存在次序關係。如下圖

 

用之前學過的數據結構來表示圖,不是很方便

鄰接矩陣

考慮到圖是由頂點和邊或弧兩部分組成。合在一起比較困難,那就很自然地考慮到分兩個結構來分別存儲。頂點不分大小、主次,所以用一個一維數組來存儲是很不錯的選擇。而邊或弧由於是頂點與頂點之間的關係,一維搞不定,那就考慮用一個二維數組來存儲。於是鄰接矩陣誕生了啦。

圖的鄰接矩陣(Adjacency  Matrix)存儲方式是是用兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱爲鄰接矩陣)存儲圖中的邊或弧信息

設圖G有n個頂點,則鄰接矩陣是一個n X n的方陣,定義爲:


下圖是一個無向圖


對於矩陣的主對角線的值,全爲是0是因爲不存在頂點到自身的邊。比如v0到v0  ,arc[0][1]=1是因爲v0到v1的邊存在。

對稱矩陣就是n階矩陣的元滿足aij=aji(0   i,j   n)。即從矩陣的左上角到右下角的主對角線爲軸,右上角的元與左下角對應 的元全都相等。

有了這個矩陣,我們就可以很容易地知道圖中的信息。

1.    我們要判定任意兩頂點是否有邊無邊就非常容易

2.    我們要知道某個頂點的度,其實就是這個頂點vi在鄰接矩陣中第i行(或第i列)的元素之和。如頂點V1的度主是1+0+1+0=2

3.    求頂點Vi的所有鄰接點就是將矩陣中第i行元素掃描一遍,arc[i][j]爲1就是鄰接點。

我們再來看一個有向圖樣例,如下圖

 

與無向圖同樣的辦法,判斷頂點vi到vj是否存在弧,只需要查找矩陣中arc[i][j]是否爲1即可。

之前我提到網的概念,也就是每條邊上帶有權的的圖叫做網。

設圖G是網圖,有n個頂點,則鄰接矩陣是一個nXn的方陣,定義爲:


這裏wij表示(vi,vj)或<vi,vj>上的權值。∞表示一個計算機允許的、大於所有邊上權值的值,也就是一個不可能的權限值。這是由於權值wij大多數情況下是正值,因此必須要用一個不可能的值來代表不存在。權值wij大多數情況下是正值,但個別時候可能就是0,甚至有可能是負值。因此必須要用一個不可能的值來代表不存在。

如下圖:

 

typedef  char   VertexType    //頂點類型由用戶定義

typedef  int    EdgeType  //權值類型

#define  MAXVEX  100

#define  INFINITY   65535

typedef  struct

{

     VertexType   vexs[MAXVEX];

    EdgeType   arc[MAXVEX][MAXVEX];

    int   numVertexes,numEdges;

}

有了這個結構定義,我們構造一個圖其實就是給頂點表和邊表輸入數據的過程。我們來看看無向網圖的創建代碼。如下:

void  CreateMGraph(MGraph  *G)

{

     int i,j,k,w;

    printf(“輸入頂點和邊數:\n”);

    scanf(“%d,%d”,&G->numVertexes,&G->numEdges);

    for(i=0;i<G->numVertexes;i++)

         for(j=0;j<G->numVertexes;j++)

                 G->arc[i][j] =INFINITY  ; //鄰接矩陣初始化

     for(k=0;k<G->numEdges;k++)

     {

          printf(“輸入邊(vi,vi)的上標i,下標j和權w”);

          scanf(“%d,%d,%d”,&i,&j,&w);

          G->arc[i][j] = w;

          G->arc[j][i] = w;  //無向圖是對稱矩陣。

      }

}

從代碼中也可以看到,n個頂點和e條邊的無向網圖的創建,時間複雜度O(n+n2+e)。其中對鄰接矩陣G.arc的初始化耗費了O(n2)的時間。

鄰接表

鄰接矩陣是不錯的一種圖存儲結構,但是我們也發現,對於邊數相對頂點較少的圖,這種結構是存在對存儲空間的極大浪費的。

如圖

 

我們把數組與鏈表相結合的存儲方法稱爲鄰接表(Adjacency    List

處理方法如下:

1.    圖中頂點用一個一維數組存儲,當然,頂點也可以用單鏈表來存儲,不過數組可以較容易地讀取頂點信息,更加方便。另外,對於頂點數組中,每個數據元素還需要存儲指向一個鄰接點的指針,以便查找該頂點的邊信息

2.    圖中每個頂點vi的所有鄰接點構成一個線性表,由於鄰接點的個數不定,所以用單鏈表存儲,無向圖稱爲頂點vi邊表,有向圖則稱爲頂點vi作爲弧尾的出邊表。

 

 

從圖中我們知道,頂點表的各個結點由data和firstedge兩個域表示,data是數據域,存儲頂點的信息,firstedge是指針域,指向邊表的第一個結點,即此頂點的第一個鄰接點。

邊表結點由adjvex和next兩個域組成。adjvex是鄰接點域,存儲某頂點的鄰接點在頂點表中的下標,next則存儲指向邊表中下一個結點的指針。

比如:v1頂點與v0、v2互爲鄰接點,則在v1的邊表中,adjvex分別爲v0的0和v2的2。

這樣的結構,對於我們要獲得圖的相關信息也是很方便的。比如我們要想知道某個頂點的度,就去查找這個頂點的邊表中結點的個數。若要判斷頂點vi到vj是否存在邊,只需要測試頂點vi的邊表中adjvex是否存在結點vj的下標j就行了若求頂點的所有鄰接點,其實就是對此頂點的邊表進行遍歷,得到的adjvex域對應的頂點就是鄰接點。

 

若圖是有向圖,鄰接表的結構是類似的,

 

注意的是有向圖由於有方向,我們是以頂點爲弧尾來存儲邊表的,這樣很容易就可以得到每個頂點的出席。但也有時爲了便於確定頂點的入度或以頂點爲弧頭的弧,我們可以建立一個有向圖的逆鄰接表,即對我個頂點vi都建立一個鏈接爲vi爲弧頭的表。

對於帶權值的網圖,可以在邊表結點定義中再增加一個weight的數據域,存儲權值信息即可。

 

 

結構定義:

typedef  char  VertexType

typedef  int    EdgeType;  //邊上的權值類型應由用戶定義

 

typedef   struct   EdgeNode     //邊表結點

{

    int  adjvex;  //鄰接點域,存儲該頂點對應的下標

    EdgeType   weight;

    struct   EdgeNode   *next;  //指向下一個連接點

}EdgeNode;

 

typedef   struct  VertexNode   //頂點表結點

{

     VertexType   data;

    EdgeNode   *firstedge;  //邊表頭指針

}VertexNode,AdjList[MAXVEX];

 

typedef struct

{

AdjList   adjList;

int   numVertexes,numEdges;  //

}GraphAdjList;

 

建立圖的鄰接表結構,無向圖

void  CreateALGraph(GraphAdjList   *G)

{

         int  i,j,k;

         EdgeNode *e;

        printf(“輸入頂點數和邊數:\n”);

        scanf(“%d,%d”,&G->numVertexes,&G->numEdges);//輸入頂點和邊數

       for(i=0;i<G->numVertexes;i++)

       {

           scanf(&G->adjList[i].data);

           G->adjList[i].firstedge=NULL;

       }

         //建立邊表

        for(k=0;k<G->numEdges;k++)

        {

               prinf(“輸入邊(vi,vj)上的頂點序號\n”);

               scanf(“%d,%d”,&i,&j); //輸入邊(vi,vj)上的頂點序號

               e=(EdgeNode  *)malloc(sizeof(EdgeNode));

               e->adjvex = j;

              e->next=G->adjList[i].firstedge; //

               G->adjList[i].firstedge=e;

 

               e=(EdgeNode  *)malloc(sizeof(EdgeNode));

               e->adjvex = i;

              e->next=G->adjList[j].firstedge;

               G->adjList[j].firstedge = e;

        }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章