1. 程式人生 > >數據結構與算法 - 圖的鄰接表 (思想以及實現方式)

數據結構與算法 - 圖的鄰接表 (思想以及實現方式)

相對 amp 註釋 left 深度優先遍歷 bsp wid .com ebo

PS:鄰接表,存儲方法跟樹的孩子鏈表示法相類似,是一種順序分配和鏈式分配相結合的存儲結構。如這個表頭結點所對應的頂點存在相鄰頂點,則把相鄰頂點依次存放於表頭結點所指向的單向鏈表中。圖的鄰接表儲存方式相對於鄰接矩陣比較節約空間,對於鄰接矩陣需要分別把頂點和邊(頂點之間的關系)用一維數組和二維數組儲存起來。而鄰接表則是把頂點按照順序儲存到一維數組中,然後再通過鏈式方式,把有關系的頂點下標鏈接到後方,咱們先不考慮權重問題,結構體定義簡單一點,當然加上權值也不難。下方看圖解釋。

鄰接表

  1. 有向圖
  2. 無向圖

逆鄰接表

  1. 有向圖

鄰接表實現步驟

  1. 結構體
  2. 創建圖
    1. 頂點和邊數,頂點需要用一維數組保存
    2. 獲取頂點的下標,因為鏈接結點中的index域是頂點的下標值。
    3. 創建結點,通過頭插法(或尾插法)把結點鏈接到頭結點的尾部
  3. 打印(遍歷方式後序介紹)

1:結構體

我們可以分為頭和表結構,如圖所示 技術分享圖片

那麽結構體就可以這樣設計

/**
* 表頭連接的表中結點定義
* */
typedef struct tableBody {
    int vexIndex;//鄰接點在數組中的位置下標
    struct tableBody *nextarc;//指向下一個鄰接點的指針
} tableBody;
/**
 * 表頭結點定義
 * */
typedef struct tableHead {
    char data;//頂點的數據域
    struct tableBody *firstarc;//
指向鄰接點的指針 } tableHead, *tableHeadArr;//存儲各鏈表頭結點的數組 /**圖-鄰接表定義*/ typedef struct { tableHead vertices[20];//圖中頂點及各鄰接點數組 int vexnum, arcnum;//記錄圖中頂點數和邊或弧數 } LJBGraph;

2:創建表

內部註釋涵蓋了上述步驟。
void createGraph(LJBGraph *g) {
    //總頂點個數,總邊數
    int i, j, k;
    tableBody *tb;
    printf("輸入頂點數和邊數");
    scanf(
"%d %d", &g->vexnum, &g->arcnum);//獲取頂點數和邊數 //gettchar(); for (i = 0; i < g->vexnum; i++) { char c; printf("輸入%d 個頂點值", (i + 1)); getchar(); scanf("%c", &c); g->vertices[i].data = c; //獲取頂點值, g->vertices[i].firstarc = NULL; //將邊表置為空 } for (k = 0; k < g->arcnum; k++) { printf("輸入a,b 在圖中有a-->b:"); getchar(); char a,b; scanf("%c %c", &a, &b); //輸入i,j 在圖中有i-->j tb = (tableBody *) malloc(sizeof(tableBody)); i=localIndex(g,a); //a頂點所在頂點數組中的下標值。 j=localIndex(g,b); //b頂點所在數組中的下標值。 tb->vexIndex = j; tb->nextarc = g->vertices[i].firstarc; //頭插法建立邊表 g->vertices[i].firstarc = tb;//把新建的結點鏈接在頂點後面 /*如果為無向圖,則加入以下代碼 e=(EdgeNode*)malloc(sizeof(EdgeNode)); e->adjvex = i; e->next = g->adjList[j].firstedge; g->adjList[j].firstedge= e;*/ } printfL(g); DFSTraverse(g); }
尋找下標值,就是普通的遍歷,在頂點數組中遍歷返回下標。
int localIndex(LJBGraph *g,char data){
    for(int i=0;i<g->vexnum;i++){
        if(g->vertices[i].data == data){
            return i;
        }
    }
    printf("沒有這個字符");
    return -1;
}

所得有向圖和無向圖的結構圖不一樣

技術分享圖片 技術分享圖片

3:打印

void printfL(LJBGraph *g) {
    //輸出圖的信息
    printf("表為 :\n");
    tableBody *p;
    printf("\n");
    //鄰接表不需要表標題。
    int i;
    for (i = 0; i < g->vexnum; i++) {
        printf("%d%c\t",(i),g->vertices[i].data);//表頭結點
        p = g->vertices[i].firstarc;
        while (p) {
            printf("%d\t", p->vexIndex);//外表結點
            p = p->nextarc;//外表結點下移
        }
        printf("\n");
    }

}

主方法

是不是代碼很簡單,所有東西都封裝起來。
int main(void) {
    LJBGraph *g;
    createGraph(g);
    return 0;
}

註:比較鄰接矩陣和鄰接表的區別

存儲結構 儲存方式 操作特性
鄰接矩陣 一維數組(頂點) 二維數組(鄰接關系) 1:易於判定頂點是否鄰接,查頂點的鄰接點 2:插入、刪除頂點復雜
鄰接表 頭結點(頂點) 表結點(鄰接關系) 1:易於:查詢某頂點的鄰接點,邊或弧的插入、刪除 2:判定頂點是否鄰接,比鄰接矩陣低效。

4:逆鄰接表

所謂逆鄰接表就是方向相反的鏈接到頂點後面,一看圖便知。

技術分享圖片

完:

下一篇講會講解深度優先遍歷和廣度優先遍歷基本使用和思想。

數據結構與算法 - 圖的鄰接表 (思想以及實現方式)