圖(鄰接矩陣)


  圖有兩種表示方法,鄰接矩陣和鄰接表,接下來我們講解鄰接矩陣和用c實現一個鄰接矩陣.

我們先看一個圖:

我們想將這樣一個圖信息存儲起來,我們有兩個必須存儲的數據,節點信息(a,b,c,d,e)和權值(3,5,4,1,6,7)和節點之間的關系.權值也就是路徑.

鄰接矩陣表示法,用兩個數組表示,一個一維數組和一個二維數組.

一維數組存儲節點信息,二維數組存儲節點之間的關系.

將上圖轉換成一個鄰接矩陣

接下來我們看一看鄰接矩陣是的結構體.

typedef struct matrix
{
    node_type vertex[MAX_NUM];//節點信息
    int arcs[MAX_NUM][MAX_NUM];//矩陣
    int vertexs, brim;//節點數,邊數
} Graph;

我們將node_type設為char類型,方便明了.但在輸入字符后,我們通常回輸出空格或者回車符,在向字符變量輸入字符時,我們要將之前輸入的空格或者回車符從緩沖區中清楚,這也就是程序中getchar()的用處.

我們先構造一個鄰接矩陣

1)輸入節點數和節點信息,構造上圖的兩個數組.(將二維數組初始化MAX)

2)輸入某個節點的相鄰節點和權值(通過下標找到在二維數組中相應的位置,將權值輸入)

void g_create(Graph * graph)
{
    int num;
    int i, j, k;
    char c;

    printf("輸入節點個數:");
    scanf("%d", &graph->vertexs);
    getchar();//接受回車鍵

    printf("輸入節點信息:");
    for ( i = 0; i < graph->vertexs; i++ )
    {
        scanf("%c", &graph->vertex[i]);
        getchar();
    }

    for ( i = 0; i < graph->vertexs; i++ )//初始化矩陣
        for ( j = 0; j < graph->vertexs; j++ )
            graph->arcs[i][j] = MAX_VALUE;
    graph->brim = 0;//初始化邊數

    // i 代表行數, j 是用來循環的, k 代表列數
    for ( i = 0; i < graph->vertexs; i++ )
    {
        printf("輸入與%c節點相鄰的節點與權值,輸入#號鍵結束\n", graph->vertex[i]);
        for ( j = 0; j < graph->vertexs; j++ )
        {
            scanf("%c", &c);
            if ( c == '#' )
            {
                getchar();
                break;
            }
            scanf("%d", &num);
            for ( k = 0; k < graph->vertexs; k++ )
            {
                if ( graph->vertex[k] != c )
                    continue;
                graph->arcs[i][k] = num;
                graph->brim++;
            }
            getchar();
        }
    }
    graph->brim /= 2;
}

// i 代表行數, j 是用來循環的, k 代表列數"下面的代碼是將輸入的邊和權值的信息存儲在二維數組中.

i 代表的是在一維數組中的節點的位置,也是二維數組中行.(例如:與a相鄰的節點有兩個,b和c.這時i=0,代表以為數組中第一個位置,j用來循環輸入邊和權值,因為我們不知道每個節點都有多少個相鄰節點.當輸入'#'號時結束輸入.k是為了將輸入的相鄰節點與一維數組中的節點信息匹配,如果存在這個節點,這時將權值輸入到二維數組中,而 i 是二維數組的行, k 是二維數組的列.然后我們將邊數+1,因為a 的相鄰節點是 b ,而 b 的相鄰節點是 a, 這時我們對邊增加了兩次,所以在構造鄰接矩陣最后將邊數除以2.)

深度優先遍歷

深度優先遍歷是通過遞歸來實現,與二叉樹的遍歷有點像.

1)以某一節點開始,訪問該節點

2)以該節點開始,重復1.(這里需要定義一個節點數大小的bool類型數組,來記錄哪些節點訪問過了,哪些節點沒有訪問過.)

3)當某個節點的鄰接節點都訪問過了,回退到上一個節點,訪問上一個節點的其他相鄰節點.

4)重復3.直至返回開始節點.

這是連通圖的遍歷方法,對於非連通圖,我們只需循環調用遞歸,直至所有節點都訪問過.

實現算法:

1)先創建一個visited數組,初始化為false.

2)調用遍歷函數,實現遞歸.

3)當相鄰節點為false時,以該節點進行遞歸.

4)否則返回上一節點

 1 //深度優先遍歷
 2 static void dfs_graph(Graph * graph, bool visited[], const int i);
 3 void g_depth_first_search(Graph * graph)
 4 {
 5     bool visited[graph->vertexs];
 6     int i;
 7     for ( i = 0; i < graph->vertexs; i++ )
 8         visited[i] = false;
 9     visited[0] = true;
10     dfs_graph(graph, visited, 0);
11     printf("\n");
12 }
13 
14 static void dfs_graph(Graph * graph, bool visited[], const int i)
15 {
16     int j;
17     printf("%c\t", graph->vertex[i]);
18     for ( j = 0; j < graph->vertexs; j++ )//依次檢查矩陣
19     {
20         if ( graph->arcs[i][j] != MAX_VALUE && !visited[j] )//i 代表矩陣的行, j 代表矩陣的列
21         {
22             visited[j] = true;
23             dfs_graph(graph, visited, j);
24         }
25     }
26 }

 圖例:

廣度優先遍歷

廣度優先遍歷通過隊列實現.

1)從摸一節點開始,將該節點入隊,找到該節點的所有相鄰節點,將他們入隊.

2)將該節點出隊,再將隊頭節點的所有相鄰節點入隊.(這里也需要一個visited數組,已經入隊過的節點不再入隊.)

3)檢查隊列,對隊頭元素進行操作,出隊.

 1 void g_breadth_first_search(Graph * graph)
 2 {
 3     Queue queue;//隊列存儲的是節點數組的下標(int)
 4     bool visited[graph->vertexs];
 5     int i, pos;
 6 
 7     q_init(&queue);
 8     for ( i = 0; i < graph->vertexs; i++ )
 9         visited[i] = false;
10     
11     visited[0] = true;
12     q_push(&queue, 0);
13     while ( !q_empty(&queue) )
14     {
15         pos = q_front(&queue);
16         printf("%c\t", graph->vertex[pos]);
17         for ( i = 0; i < graph->vertexs; i++ )//把隊頭元素的鄰接點入隊
18         {
19             if ( !visited[i] && graph->arcs[pos][i] != MAX_VALUE )
20             {
21                 visited[i] = true;
22                 q_push(&queue, i);
23             }
24         }
25         q_pop(&queue);
26     }
27     printf("\n");
28 }

如果隊列有問題的同學,可以參考我之前寫的隊列,我把隊列文件直接拷貝過來了.

源碼

graph.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>

#include "aqueue.h"

#define MAX_VALUE INT_MAX
#define MAX_NUM 100

typedef char node_type;

typedef struct matrix
{
    node_type vertex[MAX_NUM];//節點信息
    int arcs[MAX_NUM][MAX_NUM];//矩陣
    int vertexs, brim;//節點數,邊數
} Graph;

void g_create(Graph * graph)
{
    int num;
    int i, j, k;
    char c;

    printf("輸入節點個數:");
    scanf("%d", &graph->vertexs);
    getchar();//接受回車鍵

    printf("輸入節點信息:");
    for ( i = 0; i < graph->vertexs; i++ )
    {
        scanf("%c", &graph->vertex[i]);
        getchar();
    }

    for ( i = 0; i < graph->vertexs; i++ )//初始化矩陣
        for ( j = 0; j < graph->vertexs; j++ )
            graph->arcs[i][j] = MAX_VALUE;
    graph->brim = 0;//初始化邊數

    // i 代表行數, j 是用來循環的, k 代表列數
    for ( i = 0; i < graph->vertexs; i++ )
    {
        printf("輸入與%c節點相鄰的節點與權值,輸入#號鍵結束\n", graph->vertex[i]);
        for ( j = 0; j < graph->vertexs; j++ )
        {
            scanf("%c", &c);
            if ( c == '#' )
            {
                getchar();
                break;
            }
            scanf("%d", &num);
            for ( k = 0; k < graph->vertexs; k++ )
            {
                if ( graph->vertex[k] != c )
                    continue;
                graph->arcs[i][k] = num;
                graph->brim++;
            }
            getchar();
        }
    }
    graph->brim /= 2;
}

void g_printMatrix(Graph * graph)//打印矩陣狀態
{
    int i, j;

    for ( i = 0; i < graph->vertexs; i++ )
    {
        for ( j = 0; j < graph->vertexs; j++ )
        {
            printf("%-10d ", graph->arcs[i][j]);
        }
        printf("\n");
    }
}

//深度優先遍歷
static void dfs_graph(Graph * graph, bool visited[], const int i);
void g_depth_first_search(Graph * graph)
{
    bool visited[graph->vertexs];
    int i;
    for ( i = 0; i < graph->vertexs; i++ )
        visited[i] = false;
    visited[0] = true;
    dfs_graph(graph, visited, 0);
    printf("\n");
}

static void dfs_graph(Graph * graph, bool visited[], const int i)
{
    int j;
    printf("%c\t", graph->vertex[i]);
    for ( j = 0; j < graph->vertexs; j++ )//依次檢查矩陣
    {
        if ( graph->arcs[i][j] != MAX_VALUE && !visited[j] )//i 代表矩陣的行, j 代表矩陣的列
        {
            visited[j] = true;
            dfs_graph(graph, visited, j);
        }
    }
}

//廣度優先遍歷
void g_breadth_first_search(Graph * graph)
{
    Queue queue;//隊列存儲的是節點數組的下標(int)
    bool visited[graph->vertexs];
    int i, pos;

    q_init(&queue);
    for ( i = 0; i < graph->vertexs; i++ )
        visited[i] = false;
    
    visited[0] = true;
    q_push(&queue, 0);
    while ( !q_empty(&queue) )
    {
        pos = q_front(&queue);
        printf("%c\t", graph->vertex[pos]);
        for ( i = 0; i < graph->vertexs; i++ )//把隊頭元素的鄰接點入隊
        {
            if ( !visited[i] && graph->arcs[pos][i] != MAX_VALUE )
            {
                visited[i] = true;
                q_push(&queue, i);
            }
        }
        q_pop(&queue);
    }
    printf("\n");
}

int main(void)
{
    Graph graph;
    g_create(&graph);
    g_printMatrix(&graph);
    g_depth_first_search(&graph);
    g_breadth_first_search(&graph);

    return 0;
}

 


免責聲明!

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



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