第8章 圖


第8章 圖

數據結構與算法_師大完整教程目錄(更有python、go、pytorch、tensorflow、爬蟲、人工智能教學等着你):https://www.cnblogs.com/nickchen121/p/13768298.html

一、圖的基本概念

  1. \(G\) 的頂點集:記作 \(V(G)\)

  2. \(G\) 的邊集:記作 \(E(G)\)

  3. 無向圖的邊:頂點 \(v\) 到 頂點 \(u\) 的邊記作 \((u,v)\)

  4. 有向圖的邊:頂點 \(v\) 到 頂點 \(u\) 的邊記作 \(<u,v>\)

  5. 鄰接點:若 \((v,u)\) 是一條無向邊,則稱 \(u\)\(v\) 互為鄰接點

  6. 頂點和邊數的關系:

    1. \(n\) 個頂點的無向圖,其邊數 \(e\) 小於等於 \(n(n-1)/2\)邊數恰好為 \(n(n-1)/2\) 的無向圖稱為無向完全圖
    2. \(n\) 個頂點的有向圖,其邊數 \(e\) 小於等於 \(n(n-1)\)邊數恰好為 \(n(n-1\) 的有向圖稱為有向完全圖
  7. 無向圖頂點的度:頂點相關聯的邊的數目,頂點 \(v\) 的度記為 \(D(v)\)

  8. 有向圖頂點的度:

    1. 入度:以頂點 \(v\) 作為終點的邊的數目,記為 \(ID(v)\)
    2. 出度:以頂點 \(v\) 作為始點的邊的數目,記為 \(OD(v)\)
    3. 注:有向圖頂點的度等於頂點的入度和出度之和
  9. 頂點數 \(n\)、邊數 \(e\) 和度數的關系:\(e=\frac{1}{2}\sum_{i=1}^n{D(v_i)}\)

  10. 有向圖路徑:存在一個頂點序列 \(v,v_1,v_2,\cdots,v_m,u\),且 \((v,v_1),(v_1,v_2),\cdots,(v_m,u)\) 都屬於 \(E(G)\),則該頂點序列為 \(v\)\(u\) 的一條路徑

  11. 無向圖路徑:有向圖路徑:存在一個頂點序列 \(v,v_1,v_2,\cdots,v_m,u\),且 \(<v,v_1>,<v_1,v_2>,\cdots,<v_m,u>\) 都屬於 \(E(G)\),則該頂點序列為 \(v\)\(u\) 的一條路徑

  12. 簡單路徑:一條路徑除了起點 \(v\) 和終點 \(u\) 之外,其余頂點均不相同,該路徑稱之為一條簡單路徑

  13. 簡單回路(簡單環):起點和終點相同\(v=u\))的簡單路徑

  14. 無向圖連通的概念:

    1. 無向圖的連通:頂點 \(v\)\(u\) 之間有路徑,則稱 \(v\)\(u\) 是連通的
    2. 連通圖(無向圖):\(V(G)\) 中任意兩個不同的頂點 \(v\)\(u\) 都連通(即有路徑),則 \(G\) 為連通圖
    3. 連通分量:無向圖 \(G\)極大連通子圖(任何連通圖的連通分量都是其本身,非連通的無向圖有多個連通分量
  15. 有向圖連通的概念:

    1. 強連通圖:\(V(G)\) 中任意兩個不同的頂點 \(v\)\(u\)都存在從 \(v\)\(u\) 和從 \(u\)\(v\) 的路徑
    2. 強連通分量:有向圖的極大強連通子圖(任何強連通圖的唯一強連通分量是其自身,非強連通的有向圖有多個強連通分量
  16. 連通分量和強連通分量注意事項:單個頂點可以屬於一個單獨的連通分量或強連通分量

  17. 權:圖的每條邊上附上相關的數值

  18. 網絡(帶權圖):圖的每條邊都賦上一個權

二、圖的基本運算

三、圖的基本存儲結構

  1. 注:對於以下圖的存儲結構,都假定頂點序號從 \(0\) 開始,圖 \(G\) 的頂點集一般形式為 \(V(G)=\{v_0,\cdots,v_i,\cdots,v_{n-1}\}\)

3.1 鄰接矩陣及其實現

  1. 圖的鄰接矩陣表示法:用兩個表格分別存儲數據元素(頂點)的信息和數據之間的關聯(邊)信息

    1. 一維數組(順序表):存儲數據元素的信息
    2. 二維數組(鄰接矩陣):存儲數據元素之間的關系
  2. 鄰接矩陣的性質:若 \((v_i,v_j)或<v_i,v_j>\in{E(G)}\)\(A[i,j]=1\);若 \((v_i,v_j)or<v_i,v_j>\notin{E(G)}\)\(A[i,j]=0\)

  3. 無向圖的鄰接矩陣:該鄰接矩陣一定是對稱的,可以采用上(下)三角矩陣進行壓縮存儲,存儲空間為 \(n(n+1)/2\)

  4. 有向圖的鄰接矩陣:該鄰接矩陣不一定是對稱的,存儲空間為 \(n^2\)

  5. 無向圖頂點 \(v_i\) 的度數:\(D(v_i) = \sum_{j=0}^{n-1}A[i,j]=\sum_{j=0}^{n-1}A[j,i]\) (對稱矩陣\(A=A^T\)

  6. 有向圖頂點 \(v_i\) 的度數:

    1. 出度:\(OD(v_i)=\sum_{j=0}^{n-1}A[i,j]\)
    2. 入度:\(ID(v_i) = \sum_{j=0}^{n-1}A[j,i]\)
  7. 圖鄰接矩陣圖:

  8. 網絡的鄰接矩陣性質:

\[A[i,j]= \begin{cases} W_{ij} &\qquad \text{當$(v_i,v_j)$或$<v_i,v_j>$$\in$E(G)} \\ 0 &\qquad \text{當$(v_i,v_j)$或$<v_i,v_j>$$\notin$E(G)且i=j} \\ \infin &\qquad \text{當$(v_i,v_j)$或$<v_i,v_j>$$\notin$E(G)且i$\neq$j} \end{cases} \]

  1. 圖網絡鄰接矩陣圖:

3.1.1 鄰接矩陣存儲結構

#define FINITY 5000 // 用5000表示無窮大
#define M 20 // 最大頂點數
typedef char vertextype; // 頂點值類型
typedef int edgetype; // 權值類型
typedef struct {
    vertextype vexs[M]; // 頂點信息域
    edgetype edges[M][M]; // 鄰接矩陣
    int n, e; // 圖中頂點總數和邊數
} Mgraph;

3.1.2 建立網絡的鄰接矩陣算法(算法)

  1. 算法步驟:

    1. 打開文件
    2. 讀入圖中的頂點數和邊數
    3. 讀入圖中的頂點值
    4. 初始化鄰接矩陣
    5. 讀入網絡中的邊
    6. 建立無向圖鄰接矩陣
    7. 關閉文件

3.2 鄰接表及其實現

  1. 鄰接表與鄰接矩陣的區別:

    1. 頂點個數為 \(n\) 的圖,鄰接矩陣的存儲空間為 \(n^2\) ,而使用鄰接表則可節省很多空間
    2. 鄰接矩陣表示法唯一,而鄰接表的表示法不唯一
  2. 鄰接表中的兩個結點:

    1. 表頭結點:存儲頂點的數據域(\(vertex\))和頭指針域(\(firstedge\)
    2. 邊表結點:鄰接點域(\(adjvex\))和鏈域(\(next\)
  3. 鄰接表的隨機訪問任意頂點的構造:讓所有頭結點順序存儲在一個向量中

  4. 圖無向圖鄰接表:

  5. 有向圖

    1. 出邊表(鄰接表):表結點存儲以頂點 \(v\) 為始點射出的一條邊
    2. 入邊表(逆鄰接表):表結點存儲以頂點 \(v\) 為終點射出的一條邊
  6. 圖有向圖的鄰接表:

  7. 鄰接表存儲空間:無向圖(\(n\) 個頂點和 \(e\) 條邊)用鄰接表存儲需要 \(n\) 個頭結點和 \(2e\) 個邊結點

  8. 注:\(e\) 遠小於 \(n(n-1)/2\),鄰接表存儲圖比鄰接矩陣存儲圖節省空間

  9. 無向圖的度(鄰接表):頂點 \(v_i\) 的度為第 \(i\) 個鏈表中結點的個數

  10. 有向圖的度(鄰接表-出邊表):

    1. 出度:第 \(i\) 個鏈表中的結點的個數
    2. 入度:所有鏈表中其鄰接點域的值為 \(i\) 的結點的個數

3.2.1 鄰接表存儲結構

#define M 20 // 預定義圖的最大頂點數
typedef char datatype; // 定點信息數據域
// 邊表結點類型
typedef struct node {
    int adjvex; // 鄰接點
    struct node *next;
} edgenode;
// 頭結點類型
typedef struct vnode {
    datatype vertex; // 頂點信息
    edgenode *firstedge; // 鄰接鏈表頭指針
} vertexnode;
// 鄰接表類型
typedef struct {
    vertexnode adjlist[M]; // 存放頭結點的順序表
    int n, e; // 圖的頂點數與邊數
} linkedgraph;

3.2.2 建立無向圖的鄰接表算法(算法)

  1. 算法步驟:

    1. 打開文件
    2. 讀入頂點數和邊數
    3. 讀入頂點信息
    4. 邊表置為空
    5. 循環 e(邊數) 次建立邊表

輸入無序對 \((i,j)\)
鄰接點序號為 \(j\)
將新結點 \(*s\) 插入頂點 \(v_i\) 的邊表頭部
9. 關閉文件

3.3 鄰接多重表

  1. 鄰接多重表由兩個表組成:

    1. 表頭結點:\(vertex\) 域存儲頂點的相關信息;\(firstedge\) 存儲第一條關聯於 \(vertex\) 頂點的邊
    2. 邊表結點:\(mark\) 域標志圖的遍歷算法中是否被搜索過;\(vex_i\)\(vex_j\) 表示邊的兩個頂點在圖中的位序;\(link_i\)\(link_j\) 表示兩個邊表結點指針。

\(link_i\) 指向關聯於頂點 \(vex_i\)的下一條邊;\(link_j\) 指向關聯於頂點 \(vex_j\) 的下一條邊
4. 邊表結點的順序如下表所示:

\(mark\) \(vex_i\) \(link_i\) \(vex_j\) \(link_j\)

四、圖的遍歷

4.1 深度優先遍歷(DFS)

  1. 深度優先遍歷步驟:

    1. 選取一個源點 \(v\in{V}\),把 \(v\) 標記為已被訪問
    2. 使用深度優先搜索方法依次搜索 \(v\) 的所有鄰接點 \(w\)
    3. 如果 \(w\) 未被訪問,以 \(w\) 為新的出發點繼續進行深度優先遍歷
    4. 如果從 \(v\) 出發,有路的頂點都被訪問過,則從 \(v\) 的搜索過程結束
    5. 如果圖中還有未被訪問過的頂點(該圖有多個連通分量或者強連通分量),則再任選一個未被訪問的頂點開始新的搜索
  2. 注:深度優先遍歷的順序是不一定的,但是,當采用鄰接表存儲結構並且存儲結構已確定的情況下,遍歷的結果將是確定的

  3. 圖深度優先遍歷:

4.2 廣度優先遍歷 (BFS)

  1. 廣度優先遍歷步驟:

    1. 從圖中某個源點 \(v\) 出發
    2. 訪問頂點 \(v\) 后,盡可能先橫向搜索 \(v\) 的所有鄰接點
    3. 在訪問 \(v\) 的各個鄰接點 \(w_k,\cdots,w_k\) 后,從這些鄰接點出發依次訪問與 \(w_1,\cdots,w_k\) 鄰接的所有未曾訪問過的頂點
    4. 如果 \(G\) 是連通圖,訪問完成;否則另選一個尚未訪問的頂點作為新源點繼續上述搜索過程,知道圖 \(G\) 的所有頂點均被訪問
  2. 注:廣度優先遍歷無向圖的過程中,調用 \(bfs(函數:從頂點\,i\,出發廣度優先遍歷圖\,g\,的連通分量)\) 的次數就是該圖連通分量的個數

  3. 圖廣度優先遍歷:

五、生成樹與最小生成樹 (無向網)

  1. 生成樹:對於一個無向的連通圖 \(G\),設 \(G'\) 是它的一個子圖,如果 \(G'\) 中包含了 \(G\) 中所有的頂點,且 \(G'\) 是無回路的連通圖,則稱 \(G'\)\(G\) 的一顆生成樹
  2. 圖生成樹:
  3. 生成森林:如果 \(G\) 是非連通的無向圖,需要若干次從外部調用 \(DFS(或BFS)\) 算法才能完成對 \(G\) 的遍歷。每一次外部調用,只能訪問 \(G\) 的一個連通分量,經過該連通分量的頂點和邊構造出一顆生成樹,\(G\) 的各個連通分量的生成樹組成了 \(G\) 的生成森林
  4. 圖生成森林:

5.1 最小生成樹的定義

  1. 生成樹的權:生成樹 \(T\) 的各邊的權值總和,記作 \(W(T)=\sum_{(u,v)\in{E}}w_{uv}\),其中 \(E\) 表示 \(T\) 的邊集,\(w_{uv}\) 表示邊 \((u,v)\) 的權

  2. 最小生成樹的權:總權值 \(W(T)\) 最小的生成樹稱為最小生成樹

  3. 構造最小生成樹的准則:

    1. 必須只使用該網絡中的邊來構造最小生成樹
    2. 必須使用且僅使用 \(n-1\) 條邊來連接網絡中的 \(n\) 個頂點
    3. 不能使用產生回路的邊

5.2 最小生成樹的普里姆(Prim)算法

  1. 兩棲邊:假設 \(G=(V,E)\) 是一個連通圖,\(U\) 是頂點集 \(V\) 的一個非空真子集,\((u,v)\) 是滿足 \(u\in{U},v\in{V-U}\) 的邊(稱這個邊為兩棲邊),且 \((u,v)\)所有的兩棲邊中具有最小的權值(此時,稱 \((u,v)\) 為最小兩棲邊)

  2. Prim算法步驟:

    1. 初始化 \(U=\{u_0\},TREE=\{\}\)
    2. 如果 \(U=V(G)\),則輸出最小生成樹 \(T\),並結束算法
    3. 在所有兩棲邊中找一條權最小的邊 \((u,v)\)(若候選兩棲邊中的最小邊不止一條,可任選其中的一條),將邊 \((u,v)\) 加入到邊集 \(TREE\) 中,並將頂點 \(v\) 並入集合 \(U\)
    4. 由於新頂點的加入,\(U\) 的狀態發生變化,需要對 \(U\)\(V-U\) 之間的兩棲邊進行調整
    5. 轉步驟 \(2\)
  3. 下圖步驟順序(\(V=\{A,B,C,D,E,F\}\)):

    1. \(U = \{A\}\)\(V-U=\{B,C,D,E,F\}\)\(TREE=\{\}\),兩棲邊 \((A,B),(A,C),(A,D),(A,E),(A,F)\),最小兩棲邊 \((A,B)\)
    2. \(U = \{A,B\}\)\(V-U=\{C,D,E,F\}\)\(TREE=\{(A,B)\}\),兩棲邊 \((B,C),(B,D),(B,F),(A,E)\)\((B,C)\) 比 (\(A,C)\) 小,因此做一個替換),最小兩棲邊 \((B,D)\)
    3. \(U = \{A,B,D\}\)\(V-U=\{C,E,F\}\)\(TREE=\{(A,B),(B,D)\}\),兩棲邊 \((B,C),(B,F),(A,E)\),最小兩棲邊 \((B,F)\)
    4. \(U = \{A,B,D,F\}\)\(V-U=\{C,E\}\)\(TREE=\{(A,B),(B,D),(B,F)\}\),兩棲邊 \((B,C),((F,E)\),最小兩棲邊 \((B,C)\)
    5. \(U = \{A,B,D,F,C\}\)\(V-U=\{E\}\)\(TREE=\{(A,B),(B,D),(B,F),(B,C)\}\),兩棲邊 \((F,E)\),最小兩棲邊 \((F,E)\)
    6. \(U = \{A,B,D,F,C,E\}\)\(V-U=\{\}\)\(TREE=\{(A,B),(B,D),(B,F),(B,C),(F,E)\}\),兩棲邊 \(\{\}\),最小兩棲邊 \(\{\}\)
  4. 圖prim算法:

5.3 最小生成樹的克魯斯卡爾(Kruskal)算法

  1. 算法步驟:

    1. 將圖 \(G\) 看做一個森林,每個頂點為一棵獨立的樹
    2. 將所有的邊加入集合 \(S\),即一開始 \(S = E\)
    3. \(S\) 中拿出一條最短的邊 \((u,v)\),如果 \((u,v)\) 不在同一棵樹內,則連接 \(u,v\) 合並這兩棵樹,同時將 \((u,v)\) 加入生成樹的邊集 \(E'\)
    4. 重復步驟 \(3\) 直到所有點屬於同一棵樹,邊集 \(E'\) 就是一棵最小生成樹
  2. 圖kruskal算法:

六、最短路徑 (有向網)

6.1 單源最短路徑(Dijkstra算法)

  1. 距離向量 \(d\):表示源點可以途徑 \(S\) 中的頂點到達 \(V-S\) 中頂點的距離

  2. 路徑向量 \(p\):保存路徑上第 \(i\) 個頂點的前驅頂點序號(沒有的話,默認為 \(-1\)

  3. 算法步驟:

    1. 找到與源點 \(v\) 最近的頂點,並將該頂點並入最終集合 \(S\)
    2. 根據找到的最近的頂點更新從源點 \(v\) 出發到集合 \(V-S\) 上可達頂點的最短路徑
    3. 重復以上操作
  4. 圖Dijkstra算法:

  5. 上圖求單源最短路徑步驟:

    1. 初始化:從源點 \(v1\) 出發得到矩陣,到達個點的最小路徑是 \(D_{12}=10\)\(D_{0}=\left[\begin{array}{cccc} v1 &v2 &v3 &v4 &v5\\ 0 &10 &\infty &30 &100\\ \end{array}\right ]\)
    2. 第一次:\(v2\)出發,\(v1\)\(v2\) 保持不變,迭代剩下點 \((v3,v4,v5)\) 的距離后,剩余點的最短路徑是 \(v4\)\(D_{1}=\left[\begin{array}{cccc} v1 &v2 &v3 &v4 &v5\\ 0 &10 &60(10+50) &30 &100\\ \end{array}\right ]\)
    3. 第二次:\(v4\) 出發,\(v1,v2,v4\) 保持不變,優化剩余點 \((v3,v5)\) 的最短距離。剩余點的最短路徑是 \(v3\)\(D_{2}=\left[\begin{array}{cccc} v1 &v2 &v3 &v4 &v5\\ 0 &10 &50(20+30) &30 &90(30+60)\\ \end{array}\right ]\)
    4. 第三次:\(v3\)出發,\(v1,v2,v4,v3\) 保持不變。優化剩余點 $v5 \(的最短路徑,\)D_{3}=\left[\begin{array}{cccc} v1 &v2 &v3 &v4 &v5\ 0 &10 &50 &30 &60(20+30+10)\ \end{array}\right ]$
  6. 源點 \(v1\)\(Dijkstra算法\) 的最短路徑(如下表):

    1. | 中間頂點 | 終點 | 路徑長度 |
      | :--: | :--: | :--: |
      | | 2 | 10 |
      | 4 | 3 | 50 |
      | | 4 | 30 |
      | 4;3 | 5 | 60 |
  7. 距離向量 \(d\) 和路徑向量 \(p\)

  8. 圖Dijkstra算法的輔助數組:

6.2 所有頂點對的最短路徑 (Floyd算法)

  1. 算法步驟:略

七、拓撲排序 (AOV網)

  1. \(AOV網\) 邊的作用:表示活動間(一個頂點表示一個活動)的優先關系

  2. 注:(真題)拓撲排序可以判斷圖中有沒有回路(深度遍歷優先算法也可以)

  3. 算法步驟

    1. 在有向圖中找一個沒有前驅(入度為 \(0\))的頂點並輸出
    2. 在圖中刪除該頂點以及所有從該頂點發出的有向邊
    3. 反復執行步驟 \(1\)\(2\),知道所有頂點均被輸出,或者 \(AOV\) 網中再也沒有入度為 \(0\) 的頂點存在為止
  4. 圖拓撲排序:

八、關鍵路徑 (AOE網)

  1. \(AOE網\) 邊的作用:表示活動(一個頂點表示一個活動)持續的時間

  2. 事件可能的最早開始時間 \(v_e(i)\):對於某一事件 \(v_i\),它可能的最早發生時間 \(v_e(i)\) 是從源點到頂點 \(v_i\) 的最大路徑長度

    1. \[\]

\[3. 事件允許的最晚發生時間 $v_l(i)$:對於某一事件 $v_i$,它允許的最晚發生時間是在保證按時完成整個工程的前提下,該事件最晚必須發生的時間 1. \]

\begin{cases}v_l(n-1)=v_e(n-1)\v_l=min{v_l(j)-len(<v_i,v_j>)}(0\leq{i}\leq{n-2})\end{cases}

\[4. **最早可能開始時間 $e(i)$ 和 允許的最晚發生時間 $l(i)$:** 1. \]

\begin{cases}e(k)=v_e(i)\l(k)=v_l(j)-len(<v_i,v_j>)\end{cases}

\[ 2. 注:$k$ 為某條邊,即 $<v_i,v_j>$ 為 $a_k$ 5. 關鍵活動:$e(i) = l(i)$ 的頂點為關鍵活動 6. 算法步驟: 1. 求出各個事件的 $v_e$ 和 $v_l$ 值后 **計算原則:$v_e$ 的值越大越好;$v_l$ 的值越小越好** 3. 求出活動的最早可能開始時間 $e(i)$ 和 允許的最晚發生時間 $l(i)$ 4. 其中滿足 $e(i)=l(i)$ 的活動就是 $AOE網$ 中的關鍵活動 7. 圖關鍵路徑圖:![](https://imgmd.oss-cn-shanghai.aliyuncs.com/c/關鍵路徑圖.jpg?x-oss-process=style/watermark) ```default 拓撲序列(v0、v1、v2、v4、v3、v6、v7、v5、v8、v9)每個事件的最早開始時間: ve(0)=0 ve(1)= 8,ve(2)=6,ve(4)=7; ve(3)=max{ve(1)+len(a3),ve(2)+len(a4)}=16; ve(6)=max{ve(2)+len(a5),ve(4)+len(a6)}=16; ve(7)=max{ve(6)+len(a11),ve(4)+len(a7)}=20; ve(5)= ve(3)+len(a8)=20; ve(8)=max{ve(3)+len(a9),ve(6)+len(a10),ve(7)+len(a12)}=35; ve(9)=max{ve(5)+len(a13),ve(8)+len(a14)}=45; 每個事件允許的最晚發生時間如下: vl(9)=ve(9)=45 vl(8)=vl(9)-len(<v8,v9>)=45-10=35 vl(5)=vl(9)-len(<v5,v9>)=45-14=31 vl(7)=vl(8)-len(<v7,v8>)=35-6=29 vl(6)=min{vl(7)-len(<v6,v7>),vl(8)-len(<v6,v8>)}=min{27,27}=27 vl(3)=min{vl(5)-len(<v3,v5>),vl(8)-len(<v3,v8>)}=min{27,16}=16 vl(4)=min{vl(6)-len(<v4,v6>),vl(7)-len(<v4,v7>)}=min{18,16}=16 vl(2)=min{vl(3)-len(<v2,v3>),vl(6)-len(<v2,v6>)}=min{6,18}=6 vl(1)=vl(3)-len(<v1,v3>)=13 vl(0)=min{vl(1)-8,vl(2)-6,vl(4)-7}=min{5,0,9}=0 ``` 8. 圖關鍵路徑![](https://imgmd.oss-cn-shanghai.aliyuncs.com/c/關鍵路徑.jpg?x-oss-process=style/watermark) 9. 圖AOE網計算:![](https://imgmd.oss-cn-shanghai.aliyuncs.com/c/AOE網計算.jpg?x-oss-process=style/watermark) # 九、算法設計題 ## 9.1 求一個頂點的度(真題)(算法) 無向圖采用鄰接表作為存儲結構,求一個頂點的度 1. 算法步驟: 1. 獲取頂點的第一個結點 $firstedge$ 2. 如果第一個結點存在,循環獲取后續結點 ```c #define M 20 // 預定義圖的最大頂點數 typedef char datatype; // 頂點信息數據域 // 邊表結點類型 typedef struct node { int adjvex; // 鄰接點 struct node *next; } edgenode; // 頭結點類型 typedef struct vnode { datatype vertex; // 頂點信息 edgenode *firstedge; // 鄰接鏈表頭指針 } vertexnode; // 鄰接表類型 typedef struct { vertexnode adjlist[M]; // 存放頭結點的順序表 int n, e; // 圖的頂點數與邊數 } adjgraph; int d(adjgraph g, int i) { int k = 0; edgenode *p; p = g.adjlist[i].firstedge; while (p) { k++; p = p->next; } return k; } ``` ## 9.2 往圖中插入一個頂點(算法) 無向圖采用鄰接表作為存儲結構,往圖中插入一個頂點 1. 算法步驟: 1. 查找並判斷待插入的結點是否存在 2. 判斷鄰接表是否已滿 3. 上述判斷都通過,則插入新頂點 ```c #define M 20 // 預定義圖的最大頂點數 typedef char datatype; // 頂點信息數據域 // 邊表結點類型 typedef struct node { int adjvex; // 鄰接點 struct node *next; } edgenode; // 頭結點類型 typedef struct vnode { datatype vertex; // 頂點信息 edgenode *firstedge; // 鄰接鏈表頭指針 } vertexnode; // 鄰接表類型 typedef struct { vertexnode adjlist[M]; // 存放頭結點的順序表 int n, e; // 圖的頂點數與邊數 } adjgraph; void insertvex(adjgraph *g, datatype x) { int i = 0, flag = 0; // 查找待插入的結點是否存在 while (!flag && i < g->n) { if (g->adjlist[i].vertex == x) flag = 1; i++; } // 判斷結點是否存在 if (flag) { printf("結點已存在!"); getch(); exit(0); } // 判斷鄰接表是否已滿 if (g->n > M) { printf("鄰接表已滿!"); exit(0); } // 插入結點 g->adjlist[g->n].vertex = x; g->adjlist[g->n].firstedge = NULL; g->n++; } ``` ## 9.3 往圖中插入一條邊(算法) 無向圖采用鄰接表作為存儲結構,往圖中插入一條邊(本題采用前插法) 1. 算法步驟: 1. 判斷邊是否已經存在 2. 在頂點 $i$ 的鏈表中插入鄰接點 $j$;在頂點 $j$ 的鏈表中插入鄰接點 $i$(使用前插法) ```c #define M 20 // 預定義圖的最大頂點數 typedef char datatype; // 頂點信息數據域 // 邊表結點類型 typedef struct node { int adjvex; // 鄰接點 struct node *next; } edgenode; // 頭結點類型 typedef struct vnode { datatype vertex; // 頂點信息 edgenode *firstedge; // 鄰接鏈表頭指針 } vertexnode; // 鄰接表類型 typedef struct { vertexnode adjlist[M]; // 存放頭結點的順序表 int n, e; // 圖的頂點數與邊數 } adjgraph; void insertedge(adjgraph *g, int i, int j) { edgenode *p, *s; int flag = 0; // 判斷邊是否存在 if (i < g->n && j < g->n) { p = g->adjlist[i].firstedge; while (!flag && p) { if (p->adjvex == j) flag = 1; p = p->next; } if (flag) { printf("邊已存在!"); getch(); exit(0); } // 插入無向邊(i,j) s = (edgenode *) malloc(sizeof(edgenode)); s->adjvex = j; // 鄰接點序號為 j s->next = g->adjlist[i].firstedge; g->adjlist[i].firstedge = s; // 將新結點*s 插入頂點 vi 的邊表頭部 s = (edgenode *) malloc(sizeof(edgenode)); s->adjvex = i; // 鄰接點序號為 i s->next = g->adjlist[j].firstedge; g->adjlist[j].firstedge = s; // 將新結點*s 插入頂點 vj 的邊表頭部 } else printf("插入邊不合法!"); } ``` # 十、錯題集 1. **(真題)**判斷一個有向圖是否存在回路可以利用**拓撲排序法和深度優先遍歷算法** 2. 在一個帶權連通圖 $G$ 中,**權值最小的邊一定包含在 $G$ 的最小生成樹中**\]


免責聲明!

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



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