基於鄰接矩陣存儲的圖的深度優先遍歷和廣度優先遍歷


圖的存儲結構相比較線性表與樹來說就復雜很多,對於線性表來說,是一對一的關系,所以用數組或者鏈表均可簡單存放。樹結構是一對多的關系,所以我們要將數組和鏈表的特性結合在一起才能更好的存放。

那么我們的圖,是多對多的情況,另外圖上的任何一個頂點都可以被看作是第一個頂點,任一頂點的鄰接點之間也不存在次序關系。

仔細觀察以下幾張圖,然后深刻領悟一下:

 

因為任意兩個頂點之間都可能存在聯系,因此無法以數據元素在內存中的物理位置來表示元素之間的關系(內存物理位置是線性的,圖的元素關系是平面的)。

如果用多重鏈表來描述倒是可以做到,但在幾節課前的樹章節我們已經討論過,純粹用多重鏈表導致的浪費是無法想像的(如果各個頂點的度數相差太大,就會造成巨大的浪費)。

 

鄰接矩陣(無向圖)

考慮到圖是由頂點和邊或弧兩部分組成,合在一起比較困難,那就很自然地考慮到分為兩個結構來分別存儲。

頂點因為不區分大小、主次,所以用一個一維數組來存儲是狠不錯的選擇。

而邊或弧由於是頂點與頂點之間的關系,一維數組肯定就搞不定了,那我們不妨考慮用一個二維數組來存儲。

 

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

 

我們可以設置兩個數組,頂點數組為vertex[4]={V0,V1,V2,V3},邊數組arc[4][4]為對稱矩陣(0表示不存在頂點間的邊,1表示頂點間存在邊)。

 

對稱矩陣:所謂對稱矩陣就是n階矩陣的元滿足a[i][j]=a[j][i](0<=i,j<=n)。即從矩陣的左上角到右下角的主對角線為軸,右上角的元與左下角相對應的元全都是相等的。

有了這個二維數組組成的對稱矩陣,我們就可以很容易地知道圖中的信息:

  1. 要判定任意兩頂點是否有邊無邊就非常容易了;
  2. 要知道某個頂點的度,其實就是這個頂點Vi在鄰接矩陣中第i行(或第i列)的元素之和;
  3. 求頂點Vi的所有鄰接點就是將矩陣中第i行元素掃描一遍,arc[i][j]為1就是鄰接點咯。

 

鄰接矩陣(有向圖)

無向圖的邊構成了一個對稱矩陣,貌似浪費了一半的空間,那如果是有向圖來存放,會不會把資源都利用得很好呢?

 

可見頂點數組vertex[4]={V0,V1,V2,V3},弧數組arc[4][4]也是一個矩陣,但因為是有向圖,所以這個矩陣並不對稱,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1沒有弧,因此arc[0][1]=0。

另外有向圖是有講究的,要考慮入度和出度,頂點V1的入度為1,正好是第V1列的各數之和,頂點V1的出度為2,正好是第V1行的各數之和。

 

鄰接矩陣(網)

在圖的術語中,我們提到了網這個概念,事實上也就是每條邊上帶有權的圖就叫網。

 

下面以此無向圖為例,使用鄰接矩陣存儲,並實現深度優先遍歷和廣度優先遍歷:

代碼如下:

#include <stdio.h>

#define MaxVex        100            //最大頂點數
#define INFINITY    65535        //表示∞
#define TRUE        1
#define    FALSE        0
typedef char        VertexType;    //頂點類型
typedef    int            EdgeType;    //權值類型
typedef int            Bool;
Bool    visited[MaxVex];

typedef struct {
    VertexType    vexs[MaxVex];            //頂點數組
    EdgeType    arc[MaxVex][MaxVex];    //鄰接矩陣
    int    numVertexes, numEdges;            //當前圖中的結點數以及邊數
}MGraph;


//廣度優先遍歷需要的循環隊列
typedef struct {
    int    data[MaxVex];
    int    front, rear;
}Queue;


/****************************************/
//隊列的相關操作

//初始化
void InitQueue(Queue *Q)
{
    Q->front = Q->rear = 0;
}

//入隊
void EnQueue(Queue *Q, int e)
{
    if ((Q->rear+1)%MaxVex == Q->front)
        return ;

    Q->data[Q->rear] = e;
    Q->rear = (Q->rear+1)%MaxVex;
}

//判空
Bool QueueEmpty(Queue *Q)
{
    if (Q->front == Q->rear)
        return TRUE;
    else
        return FALSE;
}

//出隊
void DeQueue(Queue *Q, int *e)
{
    if (Q->front == Q->rear)
        return ;
    
    *e = Q->data[Q->front];
    Q->front = (Q->front+1)%MaxVex;
}
/****************************************/


//建立圖的鄰接矩陣
void CreateMGraph(MGraph *G)
{
    int i, j, k, w;

    printf("輸入頂點數和邊數: ");
    scanf("%d%d", &G->numVertexes,&G->numEdges);
    fflush(stdin);

    printf("==============================\n");
    printf("輸入各個頂點:\n");
    for (i=0; i<G->numVertexes; ++i)
    {
        printf("頂點%d: ",i+1);
        scanf("%c", &G->vexs[i]);
        fflush(stdin);
    }

    for (i=0; i<G->numVertexes; ++i)
    {
        for (j=0; j<G->numVertexes; ++j)
            G->arc[i][j] = INFINITY;
    }

    printf("==============================\n");
    for (k=0; k<G->numEdges; ++k)
    {
        printf("輸入邊(vi, vj)中的下標i和j和權W: ");
        scanf("%d%d%d", &i,&j,&w);
        G->arc[i][j] = w;
        G->arc[j][i] = G->arc[i][j];
    }
}


//輸出
void DisMGraph(MGraph *G)
{
    int i, j, k;
    k = G->numVertexes;
    for (i=0; i<k; ++i)
    {
        for (j=0; j<k; ++j)
        {
            printf("%5d ", G->arc[i][j]);
        }
        putchar('\n');
    }
}


/****************************************/
//圖的深度優先遍歷
void DFS(MGraph G, int i)
{
    int j;
    visited[i] = TRUE;
    printf("%c ",    G.vexs[i]);

    for (j=0; j<G.numVertexes; ++j)
    {
        if (G.arc[i][j]!=INFINITY  &&  !visited[j])
            DFS(G, j);
    }
}

void DFSTraverse(MGraph G)
{
    int i;
    for (i=0; i<G.numVertexes; ++i)
        visited[i] = FALSE;

    for (i=0; i<G.numVertexes; ++i)
    {
        if (!visited[i])
            DFS(G, i);
    }

}


//圖的廣度優先遍歷
void BFSTraverse(MGraph *G)
{
    int i, j;
    Queue Q;

    for (i=0; i<G->numVertexes; ++i)
        visited[i] = FALSE;

    InitQueue(&Q);

    for (i=0; i<G->numVertexes; ++i)
    {
        if (!visited[i])
        {
            visited[i] = TRUE;
            printf("%c ", G->vexs[i]);
            EnQueue(&Q, i);

            while (!QueueEmpty(&Q))
            {
                DeQueue(&Q, &i);
                for (j=0; j<G->numVertexes; ++j)
                {
                    if (!visited[j] && G->arc[i][j]!=INFINITY)
                    {
                        visited[j] = TRUE;
                        printf("%c ", G->vexs[j]);
                        EnQueue(&Q, j);
                    }
                }
            }
        }
    }
}
/****************************************/

//程序入口
int main(){
    MGraph G;

    CreateMGraph(&G);

    printf("\n圖的深度優先遍歷為: ");
    DFSTraverse(G);    

    printf("\n圖的廣度優先遍歷為: ");
    BFSTraverse(&G);

    printf("\n");

    return 0;
}

 

運行結果截圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM