主頁 >  其他 > 【圖論】圖論及資料結構圖儲存結構的C語言代碼實作

【圖論】圖論及資料結構圖儲存結構的C語言代碼實作

2021-12-27 07:23:38 其他

圖論

    • 資料結構的圖存盤結構
      • 圖存盤結構基本常識
      • 圖存盤結構的分類
    • 什么是連通圖,(強)連通圖詳解
      • 強連通圖
    • 什么是生成樹,生成樹(生成森林)詳解
      • 生成森林
    • 圖的順序存盤結構及其C語言代碼實作
      • 圖的順序存盤結構C語言實作
    • 圖的鄰接表存盤結構詳解
      • 鄰接表計算頂點的出度和入度
    • 圖的十字鏈表存盤結構

資料結構的圖存盤結構,常用于存盤邏輯關系為 “多對多” 的資料,圖存盤結構,是重點,也是難點,

小白要想玩轉資料結構的圖,就必須穩扎穩打,死摳圖結構的每一個知識點,每一行代碼,只有這樣,才有徹底學會圖存盤結構的可能,

除了圖存盤結構的理論知識,還會穿插一些與圖存盤結構相關的常用演算法,例如克魯斯卡爾演算法,迪杰斯特拉演算法、弗洛伊德演算法等,

資料結構的圖存盤結構

我們知道,資料之間的關系有 3 種,分別是 “一對一”、“一對多” 和 “多對多”,前兩種關系的資料可分別用線性表和樹結構存盤,具有"多對多"邏輯關系資料的結構——圖存盤結構,

在這里插入圖片描述
圖 1 所示為存盤 V1、V2、V3、V4 的圖結構,從圖中可以清楚的看出資料之間具有的"多對多"關系,例如,V1 與 V4 和 V2 建立著聯系,V4 與 V1 和 V3 建立著聯系,以此類推,

與鏈表不同,圖中存盤的各個資料元素被稱為頂點(而不是節點),拿圖 1 來說,該圖中含有 4 個頂點,分別為頂點 V1、V2、V3 和 V4,

圖存盤結構中,習慣上用 Vi 表示圖中的頂點,且所有頂點構成的集合通常用 V 表示,如圖 1 中頂點的集合為 V={V1,V2,V3,V4},

注意,圖 1 中的圖僅是圖存盤結構的其中一種,資料之間 “多對多” 的關系還可能用如圖 2 所示的圖結構表示:

在這里插入圖片描述
可以看到,各個頂點之間的關系并不是"雙向"的,比如,V4 只與 V1 存在聯系(從 V4 可直接找到 V1),而與 V3 沒有直接聯系;同樣,V3 只與 V4 存在聯系(從 V3 可直接找到 V4),而與 V1 沒有直接聯系,以此類推,

因此,圖存盤結構可細分兩種表現型別,分別為無向圖(圖 1)和有向圖(圖 2),

圖存盤結構基本常識

弧頭和弧尾
有向圖中,無箭頭一端的頂點通常被稱為"初始點"或"弧尾",箭頭直線的頂點被稱為"終端點"或"弧頭",
入度和出度
對于有向圖中的一個頂點 V 來說,箭頭指向 V 的弧的數量為 V 的入度(InDegree,記為 ID(V));箭頭遠離 V 的弧的數量為 V 的出度(OutDegree,記為OD(V)),拿圖 2 中的頂點 V1來說,該頂點的入度為 1,出度為 2(該頂點的度為 3),
(V1,V2) 和 <V1,V2> 的區別
無向圖中描述兩頂點(V1 和 V2)之間的關系可以用 (V1,V2) 來表示,而有向圖中描述從 V1 到 V2 的"單向"關系用 <V1,V2> 來表示,由于圖存盤結構中頂點之間的關系是用線來表示的,因此 (V1,V2) 還可以用來表示無向圖中連接 V1 和 V2 的線,又稱為邊;同樣,<V1,V2> 也可用來表示有向圖中從 V1 到 V2 帶方向的線,又稱為弧,
集合 VR 的含義
并且,圖中習慣用 VR 表示圖中所有頂點之間關系的集合,例如,圖 1 中無向圖的集合 VR={(v1,v2),(v1,v4),(v1,v3),(v3,v4)},圖 2 中有向圖的集合 VR={<v1,v2>,<v1,v3>,<v3,v4>,<v4,v1>},
路徑和回路
無論是無向圖還是有向圖,從一個頂點到另一頂點途徑的所有頂點組成的序列(包含這兩個頂點),稱為一條路徑,如果路徑中第一個頂點和最后一個頂點相同,則此路徑稱為"回路"(或"環"),
并且,若路徑中各頂點都不重復,此路徑又被稱為"簡單路徑";同樣,若回路中的頂點互不重復,此回路被稱為"簡單回路"(或簡單環),
拿圖 1 來說,從 V1 存在一條路徑還可以回到 V1,此路徑為 {V1,V3,V4,V1},這是一個回路(環),而且還是一個簡單回路(簡單環),

在有向圖中,每條路徑或回路都是有方向的,

權和網的含義
在某些實際場景中,圖中的每條邊(或弧)會賦予一個實數來表示一定的含義,這種與邊(或弧)相匹配的實數被稱為"權",而帶權的圖通常稱為網,如圖 3 所示,就是一個網結構:

在這里插入圖片描述

子圖:指的是由圖中一部分頂點和邊構成的圖,稱為原圖的子圖,

圖存盤結構的分類

根據不同的特征,圖又可分為完全圖,連通圖、稀疏圖和稠密圖:

  • 完全圖:若圖中各個頂點都與除自身外的其他頂點有關系,這樣的無向圖稱為完全圖(如圖 4a)),同時,滿足此條件的有向圖則稱為有向完全圖(圖 4b)),

在這里插入圖片描述

具有 n 個頂點的完全圖,圖中邊的數量為 n(n-1)/2;而對于具有 n 個頂點的有向完全圖,圖中弧的數量為 n(n-1),

稀疏圖和稠密圖:這兩種圖是相對存在的,即如果圖中具有很少的邊(或弧),此圖就稱為"稀疏圖";反之,則稱此圖為"稠密圖",

稀疏和稠密的判斷條件是:e<nlogn,其中 e 表示圖中邊(或弧)的數量,n 表示圖中頂點的數量,如果式子成立,則為稀疏圖;反之為稠密圖,

什么是連通圖,(強)連通圖詳解

圖中從一個頂點到達另一頂點,若存在至少一條路徑,則稱這兩個頂點是連通著的,例如圖 1 中,雖然 V1 和 V3 沒有直接關聯,但從 V1 到 V3 存在兩條路徑,分別是 V1-V2-V3V1-V4-V3,因此稱 V1 和 V3 之間是連通的,
在這里插入圖片描述

無向圖中,如果任意兩個頂點之間都能夠連通,則稱此無向圖為連通圖,例如,圖 2 中的無向圖就是一個連通圖,因為此圖中任意兩頂點之間都是連通的,

在這里插入圖片描述
若無向圖不是連通圖,但圖中存盤某個子圖符合連通圖的性質,則稱該子圖為連通分量,

前面講過,由圖中部分頂點和邊構成的圖為該圖的一個子圖,但這里的子圖指的是圖中"最大"的連通子圖(也稱"極大連通子圖"),

如圖 3 所示,雖然圖 3a) 中的無向圖不是連通圖,但可以將其分解為 3 個"最大子圖"(圖 3b)),它們都滿足連通圖的性質,因此都是連通分量,
在這里插入圖片描述

提示,圖 3a) 中的無向圖只能分解為 3 部分各自連通的"最大子圖",

需要注意的是,連通分量的提出是以"整個無向圖不是連通圖"為前提的,因為如果無向圖是連通圖,則其無法分解出多個最大連通子圖,因為圖中所有的頂點之間都是連通的,

強連通圖

有向圖中,若任意兩個頂點 Vi 和 Vj,滿足從 Vi 到 Vj 以及從 Vj 到 Vi 都連通,也就是都含有至少一條通路,則稱此有向圖為強連通圖,如圖 4 所示就是一個強連通圖,
在這里插入圖片描述
與此同時,若有向圖本身不是強連通圖,但其包含的最大連通子圖具有強連通圖的性質,則稱該子圖為強連通分量,
在這里插入圖片描述
如圖 5 所示,整個有向圖雖不是強連通圖,但其含有兩個強連通分量,

可以這樣說,連通圖是在無向圖的基礎上對圖中頂點之間的連通做了更高的要求,而強連通圖是在有向圖的基礎上對圖中頂點的連通做了更高的要求,

什么是生成樹,生成樹(生成森林)詳解

學習連通圖的基礎上,本節學習什么是生成樹,以及什么是生成森林,

對連通圖進行遍歷,程序中所經過的邊和頂點的組合可看做是一棵普通樹,通常稱為生成樹,
在這里插入圖片描述
如圖 1 所示,圖 1a) 是一張連通圖,圖 1b) 是其對應的 2 種生成樹,

連通圖中,由于任意兩頂點之間可能含有多條通路,遍歷連通圖的方式有多種,往往一張連通圖可能有多種不同的生成樹與之對應,

連通圖中的生成樹必須滿足以下 2 個條件:

  1. 包含連通圖中所有的頂點;
  2. 任意兩頂點之間有且僅有一條通路;

因此,連通圖的生成樹具有這樣的特征,即生成樹中邊的數量 = 頂點數 - 1,

生成森林

生成樹是對應連通圖來說,而生成森林是對應非連通圖來說的,

我們知道,非連通圖可分解為多個連通分量,而每個連通分量又各自對應多個生成樹(至少是 1 棵),因此與整個非連通圖相對應的,是由多棵生成樹組成的生成森林,、
在這里插入圖片描述
如圖 2 所示,這是一張非連通圖,可分解為 3 個連通分量,其中各個連通分量對應的生成樹如圖 3 所示:
在這里插入圖片描述

注意,圖 3 中列出的僅是各個連通分量的其中一種生成樹,
因此,多個連通分量對應的多棵生成樹就構成了整個非連通圖的生成森林,

圖的順序存盤結構及其C語言代碼實作

使用圖結構表示的資料元素之間雖然具有“多對多”的關系,但是同樣可以采用順序存盤,也就是使用陣列有效地存盤圖,

使用陣列存盤圖時,需要使用兩個陣列,一個陣列存放圖中頂點本身的資料(一維陣列),另外一個陣列用于存盤各頂點之間的關系(二維陣列),

存盤圖中各頂點本身資料,使用一維陣列就足夠了;存盤頂點之間的關系時,要記錄每個頂點和其它所有頂點之間的關系,所以需要使用二維陣列,

不同型別的圖,存盤的方式略有不同,根據圖有無權,可以將圖劃分為兩大類:圖和網 ,

圖,包括無向圖和有向圖;網,是指帶權的圖,包括無向網和有向網,存盤方式的不同,指的是:在使用二維陣列存盤圖中頂點之間的關系時,如果頂點之間存在邊或弧,在相應位置用 1 表示,反之用 0 表示;如果使用二維陣列存盤網中頂點之間的關系,頂點之間如果有邊或者弧的存在,在陣列的相應位置存盤其權值;反之用 0 表示,

結構代碼表示:

#define MAX_VERtEX_NUM 20                   //頂點的最大個數
#define VRType int                          //表示頂點之間的關系的變數型別
#define InfoType char                       //存盤弧或者邊額外資訊的指標變數型別
#define VertexType int                      //圖中頂點的資料型別
typedef enum{DG,DN,UDG,UDN}GraphKind;       //列舉圖的 4 種型別
typedef struct {
    VRType adj;                             //對于無權圖,用 1 或 0 表示是否相鄰;對于帶權圖,直接為權值,
    InfoType * info;                        //弧或邊額外含有的資訊指標
}ArcCell,AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

typedef struct {
    VertexType vexs[MAX_VERtEX_NUM];        //存盤圖中頂點資料
    AdjMatrix arcs;                         //二維陣列,記錄頂點之間的關系
    int vexnum,arcnum;                      //記錄圖的頂點數和弧(邊)數
    GraphKind kind;                         //記錄圖的種類
}MGraph;

在這里插入圖片描述
例如,存盤圖 1 中的無向圖(B)時,除了存盤圖中各頂點本身具有的資料外,還需要使用二維陣列存盤任意兩個頂點之間的關系,

由于 (B) 為無向圖,各頂點沒有權值,所以如果兩頂點之間有關聯,相應位置記為 1 ;反之記為 0 ,構建的二維陣列如圖 2 所示,
在這里插入圖片描述
在此二維陣列中,每一行代表一個頂點,依次從 V1 到 V5 ,每一列也是如此,比如 arcs[0][1] = 1 ,表示 V1 和 V2 之間有邊存在;而 arcs[0][2] = 0,說明 V1 和 V3 之間沒有邊,

對于無向圖來說,二維陣列構建的二階矩陣,實際上是對稱矩陣,在存盤時就可以采用壓縮存盤的方式存盤下三角或者上三角,

通過二階矩陣,可以直觀地判斷出各個頂點的度,為該行(或該列)非 0 值的和,例如,第一行有兩個 1,說明 V1 有兩個邊,所以度為 2,

存盤圖 1 中的有向圖(A)時,對應的二維陣列如圖 3 所示:

在這里插入圖片描述
例如,arcs[0][1] = 1 ,證明從 V1 到 V2 有弧存在,且通過二階矩陣,可以很輕松得知各頂點的出度和入度,出度為該行非 0 值的和,入度為該列非 0 值的和,例如,V1 的出度為第一行兩個 1 的和,為 2 ; V1 的入度為第一列中 1 的和,為 1 ,所以 V1 的出度為 2 ,入度為 1 ,度為兩者的和 3 ,

圖的順序存盤結構C語言實作

#include <stdio.h>
#define MAX_VERtEX_NUM 20                   //頂點的最大個數
#define VRType int                          //表示頂點之間的關系的變數型別
#define InfoType char                       //存盤弧或者邊額外資訊的指標變數型別
#define VertexType int                      //圖中頂點的資料型別
typedef enum{DG,DN,UDG,UDN}GraphKind;       //列舉圖的 4 種型別
typedef struct {
    VRType adj;                             //對于無權圖,用 1 或 0 表示是否相鄰;對于帶權圖,直接為權值,
    InfoType * info;                        //弧或邊額外含有的資訊指標
}ArcCell,AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

typedef struct {
    VertexType vexs[MAX_VERtEX_NUM];        //存盤圖中頂點資料
    AdjMatrix arcs;                         //二維陣列,記錄頂點之間的關系
    int vexnum,arcnum;                      //記錄圖的頂點數和弧(邊)數
    GraphKind kind;                         //記錄圖的種類
}MGraph;
//根據頂點本身資料,判斷出頂點在二維陣列中的位置
int LocateVex(MGraph * G,VertexType v){
    int i=0;
    //遍歷一維陣列,找到變數v
    for (; i<G->vexnum; i++) {
        if (G->vexs[i]==v) {
            break;
        }
    }
    //如果找不到,輸出提示陳述句,回傳-1
    if (i>G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//構造有向圖
void CreateDG(MGraph *G){
    //輸入圖含有的頂點數和弧的個數
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    //依次輸入頂點本身的資料
    for (int i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    //初始化二維矩陣,全部歸0,指標指向NULL
    for (int i=0; i<G->vexnum; i++) {
        for (int j=0; j<G->vexnum; j++) {
            G->arcs[i][j].adj=0;
            G->arcs[i][j].info=NULL;
        }
    }
    //在二維陣列中添加弧的資料
    for (int i=0; i<G->arcnum; i++) {
        int v1,v2;
        //輸入弧頭和弧尾
        scanf("%d,%d",&v1,&v2);
        //確定頂點位置
        int n=LocateVex(G, v1);
        int m=LocateVex(G, v2);
        //排除錯誤資料
        if (m==-1 ||n==-1) {
            printf("no this vertex\n");
            return;
        }
        //將正確的弧的資料加入二維陣列
        G->arcs[n][m].adj=1;
    }
}
//構造無向圖
void CreateDN(MGraph *G){
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    for (int i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    for (int i=0; i<G->vexnum; i++) {
        for (int j=0; j<G->vexnum; j++) {
            G->arcs[i][j].adj=0;
            G->arcs[i][j].info=NULL;
        }
    }
    for (int i=0; i<G->arcnum; i++) {
        int v1,v2;
        scanf("%d,%d",&v1,&v2);
        int n=LocateVex(G, v1);
        int m=LocateVex(G, v2);
        if (m==-1 ||n==-1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj=1;
        G->arcs[m][n].adj=1;//無向圖的二階矩陣沿主對角線對稱
    }
}
//構造有向網,和有向圖不同的是二階矩陣中存盤的是權值,
void CreateUDG(MGraph *G){
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    for (int i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    for (int i=0; i<G->vexnum; i++) {
        for (int j=0; j<G->vexnum; j++) {
            G->arcs[i][j].adj=0;
            G->arcs[i][j].info=NULL;
        }
    }
    for (int i=0; i<G->arcnum; i++) {
        int v1,v2,w;
        scanf("%d,%d,%d",&v1,&v2,&w);
        int n=LocateVex(G, v1);
        int m=LocateVex(G, v2);
        if (m==-1 ||n==-1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj=w;
    }
}
//構造無向網,和無向圖唯一的區別就是二階矩陣中存盤的是權值
void CreateUDN(MGraph* G){
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    for (int i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->vexs[i]));
    }
    for (int i=0; i<G->vexnum; i++) {
        for (int j=0; j<G->vexnum; j++) {
            G->arcs[i][j].adj=0;
            G->arcs[i][j].info=NULL;
        }
    }
    for (int i=0; i<G->arcnum; i++) {
        int v1,v2,w;
        scanf("%d,%d,%d",&v1,&v2,&w);
        int m=LocateVex(G, v1);
        int n=LocateVex(G, v2);
        if (m==-1 ||n==-1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj=w;
        G->arcs[m][n].adj=w;//矩陣對稱
    }
}
void CreateGraph(MGraph *G){
    //選擇圖的型別
    scanf("%d",&(G->kind));
    //根據所選型別,呼叫不同的函式實作構造圖的功能
    switch (G->kind) {
        case DG:
            return CreateDG(G);
            break;
        case DN:
            return CreateDN(G);
            break;
        case UDG:
            return CreateUDG(G);
            break;
        case UDN:
            return CreateUDN(G);
            break;
        default:
            break;
    }
}
//輸出函式
void PrintGrapth(MGraph G)
{
    for (int i = 0; i < G.vexnum; i++)
    {
        for (int j = 0; j < G.vexnum; j++)
        {
            printf("%d ", G.arcs[i][j].adj);
        }
        printf("\n");
    }
}
int main() {
    MGraph G;//建立一個圖的變數
    CreateGraph(&G);//呼叫創建函式,傳入地址引數
    PrintGrapth(G);//輸出圖的二階矩陣
    return 0;
}

注意:在此程式中,構建無向網和有向網時,對于之間沒有邊或弧的頂點,相應的二階矩陣中存放的是 0,目的只是為了方便查看運行結果,而實際上如果頂點之間沒有關聯,它們之間的距離應該是無窮大(∞),

例如,使用上述程式存盤圖 4(a)的有向網時,存盤的兩個陣列如圖 4(b)所示:

在這里插入圖片描述
相應地運行結果為:

2
6,10
1
2
3
4
5
6
1,2,5
2,3,4
3,1,8
1,4,7
4,3,5
3,6,9
6,1,3
4,6,6
6,5,1
5,4,5
0 5 0 7 0 0
0 0 4 0 0 0
8 0 0 0 0 9
0 0 5 0 0 6
0 0 0 5 0 0
3 0 0 0 1 0

總結一下,主要詳細介紹了使用陣列存盤圖的方法,在實際操作中使用更多的是鏈式存盤結構,例如鄰接表、十字鏈表和鄰接多重表,將在后文重點介紹

圖的鄰接表存盤結構詳解

通常,圖更多的是采用鏈表存盤,具體的存盤方法有 3 種,分別是鄰接表、鄰接多重表和十字鏈表,

先講解圖的鄰接表存盤法,鄰接表既適用于存盤無向圖,也適用于存盤有向圖,

在具體講解鄰接表存盤圖的實作方法之前,先普及一個"鄰接點"的概念,在圖中,如果兩個點相互連通,即通過其中一個頂點,可直接找到另一個頂點,則稱它們互為鄰接點,

鄰接指的是圖中頂點之間有邊或者弧的存在,

鄰接表存盤圖的實作方式是,給圖中的各個頂點獨自建立一個鏈表,用節點存盤該頂點,用鏈表中其他節點存盤各自的臨界點,

與此同時,為了便于管理這些鏈表,通常會將所有鏈表的頭節點存盤到陣列中(也可以用鏈表存盤),也正因為各個鏈表的頭節點存盤的是各個頂點,因此各鏈表在存盤臨界點資料時,僅需存盤該鄰接頂點位于陣列中的位置下標即可,

例如,存盤圖 1a) 所示的有向圖,其對應的鄰接表如圖 1b) 所示:

在這里插入圖片描述
拿頂點 V1 來說,與其相關的鄰接點分別為 V2 和 V3,因此存盤 V1 的鏈表中存盤的是 V2 和 V3 在陣列中的位置下標 1 和 2,

從圖 1 中可以看出,存盤各頂點的節點結構分為兩部分,資料域和指標域,資料域用于存盤頂點資料資訊,指標域用于鏈接下一個節點,如圖 2 所示:

在這里插入圖片描述

在實際應用中,除了圖 2 這種節點結構外,對于用鏈接表存盤網(邊或弧存在權)結構,還需要節點存盤權的值,因此需使用圖 3 中的節點結構:

在這里插入圖片描述

圖 1 中的鏈接表結構轉化為對應 C 語言代碼如下:

#define  MAX_VERTEX_NUM 20//最大頂點個數
#define  VertexType int//頂點資料的型別
#define  InfoType int//圖中弧或者邊包含的資訊的型別
typedef struct ArcNode{
    int adjvex;//鄰接點在陣列中的位置下標
    struct ArcNode * nextarc;//指向下一個鄰接點的指標
    InfoType * info;//資訊域
}ArcNode;
typedef struct VNode{
    VertexType data;//頂點的資料域
    ArcNode * firstarc;//指向鄰接點的指標
}VNode,AdjList[MAX_VERTEX_NUM];//存盤各鏈表頭結點的陣列
typedef struct {
    AdjList vertices;//圖中頂點的陣列
    int vexnum,arcnum;//記錄圖中頂點數和邊或弧數
    int kind;//記錄圖的種類
}ALGraph;

鄰接表計算頂點的出度和入度

使用鄰接表計算無向圖中頂點的入度和出度會非常簡單,只需從陣列中找到該頂點然后統計此鏈表中節點的數量即可,

而使用鄰接表存盤有向圖時,通常各個頂點的鏈表中存盤的都是以該頂點為弧尾的鄰接點,因此通過統計各頂點鏈表中的節點數量,只能計算出該頂點的出度,而無法計算該頂點的入度,

對于利用鄰接表求某頂點的入度,有兩種方式:

  1. 遍歷整個鄰接表中的節點,統計資料域與該頂點所在陣列位置下標相同的節點數量,即為該頂點的入度;
  2. 建立一個逆鄰接表,該表中的各頂點鏈表專門用于存盤以此頂點為弧頭的所有頂點在陣列中的位置下標,比如說,建立一張圖 1a) 對應的逆鄰接表,如圖 4 所示:
    在這里插入圖片描述

對于具有 n 個頂點和 e 條邊的無向圖,鄰接表中需要存盤 n 個頭結點和 2e 個表結點,在圖中邊或者弧稀疏的時候,使用鄰接表要比前一節介紹的鄰接矩陣更加節省空間,

圖的十字鏈表存盤結構

前面介紹了圖的鄰接表存盤法,本節繼續講解圖的另一種鏈式存盤結構——十字鏈表法,

與鄰接表不同,十字鏈表法僅適用于存盤有向圖和有向網,不僅如此,十字鏈表法還改善了鄰接表計算圖中頂點入度的問題,

十字鏈表存盤有向圖(網)的方式與鄰接表有一些相同,都以圖(網)中各頂點為首元節點建立多條鏈表,同時為了便于管理,還將所有鏈表的首元節點存盤到同一陣列(或鏈表)中,

其中,建立個各個鏈表中用于存盤頂點的首元節點結構如圖 1 所示:
在這里插入圖片描述

從圖 1 可以看出,首元節點中有一個資料域和兩個指標域(分別用 firstin 和 firstout 表示):

  • firstin 指標用于連接以當前頂點為弧頭的其他頂點構成的鏈表;
  • firstout 指標用于連接以當前頂點為弧尾的其他頂點構成的鏈表;
  • data 用于存盤該頂點中的資料;

由此可以看出,十字鏈表實質上就是為每個頂點建立兩個鏈表,分別存盤以該頂點為弧頭的所有頂點和以該頂點為弧尾的所有頂點,

注意,存盤圖的十字鏈表中,各鏈表中首元節點與其他節點的結構并不相同,圖 1 所示僅是十字鏈表中首元節點的結構,鏈表中其他普通節點的結構如圖 2 所示:
在這里插入圖片描述
從圖 2 中可以看出,十字鏈表中普通節點的存盤分為 5 部分內容,它們各自的作用是:

  • tailvex 用于存盤以首元節點為弧尾的頂點位于陣列中的位置下標;
  • headvex 用于存盤以首元節點為弧頭的頂點位于陣列中的位置下標;
  • hlink 指標:用于鏈接下一個存盤以首元節點為弧頭的頂點的節點;
  • tlink 指標:用于鏈接下一個存盤以首元節點為弧尾的頂點的節點;
  • info 指標:用于存盤與該頂點相關的資訊,例如量頂點之間的權值;

比如說,用十字鏈表存盤圖 3a) 中的有向圖,存盤狀態如圖 3b) 所示:
在這里插入圖片描述
拿圖 3 中的頂點 V1 來說,通過構建好的十字鏈表得知,以該頂點為弧頭的頂點只有存盤在陣列中第 3 位置的 V4(因此該頂點的入度為 1),而以該頂點為弧尾的頂點有兩個,分別為存盤陣列第 1 位置的 V2 和第 2 位置的 V3(因此該頂點的出度為 2),

對于圖 3 各個鏈表中節點來說,由于表示的都是該頂點的出度或者入度,因此沒有先后次序之分,
圖 3 中十字鏈表的構建程序轉化為 C 語言代碼為:

#define  MAX_VERTEX_NUM 20
#define  InfoType int//圖中弧包含資訊的資料型別
#define  VertexType int
typedef struct ArcBox{
    int tailvex,headvex;//弧尾、弧頭對應頂點在陣列中的位置下標
    struct ArcBox *hlik,*tlink;//分別指向弧頭相同和弧尾相同的下一個弧
    InfoType *info;//存盤弧相關資訊的指標
}ArcBox;
typedef struct VexNode{
    VertexType data;//頂點的資料域
    ArcBox *firstin,*firstout;//指向以該頂點為弧頭和弧尾的鏈表首個結點
}VexNode;
typedef struct {
    VexNode xlist[MAX_VERTEX_NUM];//存盤頂點的一維陣列
    int vexnum,arcnum;//記錄圖的頂點數和弧數
}OLGraph;
int LocateVex(OLGraph * G,VertexType v){
    int i=0;
    //遍歷一維陣列,找到變數v
    for (; i<G->vexnum; i++) {
        if (G->xlist[i].data==v) {
            break;
        }
    }
    //如果找不到,輸出提示陳述句,回傳 -1
    if (i>G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//構建十字鏈表函式
void CreateDG(OLGraph *G){
    //輸入有向圖的頂點數和弧數
    scanf("%d,%d",&(G->vexnum),&(G->arcnum));
    //使用一維陣列存盤頂點資料,初始化指標域為NULL
    for (int i=0; i<G->vexnum; i++) {
        scanf("%d",&(G->xlist[i].data));
        G->xlist[i].firstin=NULL;
        G->xlist[i].firstout=NULL;
    }
    //構建十字鏈表
    for (int k=0;k<G->arcnum; k++) {
        int v1,v2;
        scanf("%d,%d",&v1,&v2);
        //確定v1、v2在陣列中的位置下標
        int i=LocateVex(G, v1);
        int j=LocateVex(G, v2);
        //建立弧的結點
        ArcBox * p=(ArcBox*)malloc(sizeof(ArcBox));
        p->tailvex=i;
        p->headvex=j;
        //采用頭插法插入新的p結點
        p->hlik=G->xlist[j].firstin;
        p->tlink=G->xlist[i].firstout;
        G->xlist[j].firstin=G->xlist[i].firstout=p;
    }
}

提示,代碼中新節點的插入采用的是頭插法,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/393925.html

標籤:其他

上一篇:【資料結構】鏈表全決議

下一篇:Python 從入門到精通:一個月速成教程到底有多狠?你能堅持下來嗎?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more