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

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:逆鄰接表

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

完:

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

相關文章
相關標籤/搜索