資料結構學習總結--圖

weixin_42426841發表於2020-12-27

擴張註釋為個人學習想法

兩種儲存結構—鄰接矩陣和鄰接表(理解演算法的前提,結構不清楚別看演算法)

1. 鄰接矩陣

//若只是為了建立二維陣列自然沒必要使用結構體定義,但往往還需要定義其頂點數和邊數等資訊
//此時使用結構體將其抽象為一個圖的資料結構就很有必要了(抽象大法好)
//先定義結點,因為結點自身可能會攜帶其他資訊
typedef struct {
    int ind;    //結點編號
    int info;    //其他資訊
}VType;
//定義鄰接矩陣
typedef struct{
    int edges[maxSize][maxSIze];    //注意到這裡不是結點的二維陣列而是邊,若是有權圖改為float
    int n,e;    //結點數,邊數
    VType vex[maxSize];    //節點資訊
}Mgraph;    //Matrix of Graph

2.鄰接表

//先定義表所連邊,即從當前結點出發到下個結點的邊(弧尾為當前結點)
typedef struct ArcNode{
    int ind;    //對應鄰接表的下標而不是資料,這也是為什麼說這是邊的資料結構
    stuct ArcNode *nextArc;
    int w;    //邊的權重
}ArcNode;

//表內元素,即圖結點
typedef struct VNode{    //其實個人更傾向於寫成adjNode,不過VNode在看圖時更清晰
    int data;    //資料
    ArcNode *firstArc;    //第一條邊
    int i,o;    //有需要的話定義出入度
}VNode;

//鄰接表
typedef struct {
    VNode adjlist[maxSize];
    int n,e;    //頂點數和邊數
}AGraph;    //Adjacent list of Graph

深度優先遍歷(DFS)

tips:利用遞迴儲存的特性使其在“搜尋”到最深處的時候可以順序回退,在其每次抵達結點時都需要對當前結點進行一個標記(很明顯即二叉樹先序遍歷思路,進入遞迴前visit()當前結點),需要設定一個全域性陣列visited()記錄一下結點的訪問資訊,防止二次訪問

int visited[maxSize]={0};
void DFS(AGrpah *G, int v){	//從v開始遍歷
    ArcNode *p = G->adjlist[v].firstArc;
    visited[v] = 1;
    while(p!=NULL){
        if(visited[p->ind]!=1) DFS(G, p);
        p = p->nextArc;
}

把所有經歷的邊保留,其他刪去(可以在 visited[] 檢測為1時定義 q 記錄當前 p ,令 p 指向下一條邊後,釋放 q 所指邊),即可得到深度優先生成樹

廣度優先演算法(BFS)–類似於二叉樹層次遍歷

  1. 假定給的初始節點 v 是鄰接表的陣列下標,初始通過圖對應的鄰接表 G->adjlist[v].first 得到“第一條”邊(即 firstArc ),並用 p 記錄下該邊
  2. p->nextArc 迴圈遍歷同層結點,直至 p==NULL ,記錄下每次新頂點的下標至佇列 queue 中,並將對應 visited 置為1
  3. 設定front,rear指標分別指向隊頭隊尾以用於出入隊,當front==rear時佇列元素清空,遍歷完成
    每次出隊的結點迴圈上述操作
void BFS(AGraph* G, int v, int visited[]){
    memset(visited,0,maxSize);    //初始化陣列
    ArcNode* p;
    int queue[maxSize], front = 0, rear = 0, j;    //j為此時出隊結點
    visit();    //visit()為對該結點操作函式,看情況進行定義
    visited[v] = 1;    //將初始結點標記已訪問
    queue(rear++) = v;    //佇列儲存的是下標,因為邊所定義的ind指向的是下標
    while(front!=rear){    //沒使用迴圈佇列
        p = G->adjlist[v].firstArc;
        j = queue[front++];    //出隊
        while(p!=NULL){    //層次遍歷
            if(visited[p.ind]!=1){
                visit();
                queue[rear++] = p.ind;    //將未訪問過的結點入隊
                visited[p.ind] = 1;    //標記已訪問
            }
        p = p->nextArc;    
        }
    }
}

實際上以上 DFS 和 BFS 僅適用於連通分量(即極大連通子圖)只有一個的情況下,不過要適用多個非常簡單,外層套一個迴圈在發現 visited 值不為1時再次使用函式即可//以DFS為例,BFS一樣

void DFS_Traverse(Agraph *G){
    for(int v=0;v<G.n;v++){
        if(!visited[v]) DFS(G,v);    //!visited[v]即visited[v]==0
    }
}

知乎同鏈,有誤可評論指正

相關文章