01數據結構-圖的鄰接矩陣和遍歷

01數據結構-圖的鄰接矩陣和遍歷

  • 1.圖的遍歷
    • 1.1深度優先遍歷
    • 1.2廣度優先搜索
  • 2.鄰接矩陣的代碼實現

1.圖的遍歷

1.1深度優先遍歷

深度優先搜索的過程類似于樹的先序遍歷,首先從例子中體會深度優先搜索,例如下圖1是個無向圖,采用深度優先算法遍歷這個圖的過程是:

圖1
圖1

  • 1.首先任意位置找一個未遍歷過的頂點,例如從v1開始,由于v1率先訪問過了,所以,需要標記v1的狀態為訪問過;
  • 2.然后遍歷v1的鄰接點,例如訪問v2,并做標記,然后訪問v2的鄰接點,例如v4(做標記),然后v8,然后v5;
  • 3.當繼續遍歷v5的鄰接點時,根據之前的標記顯示,所有鄰接點都被訪問過了。此時,從v5回到v8,看v8是否有未被訪問過的鄰接點,如果沒有,繼續回到v4,v2,v1;
  • 4.通過查看v1,找到一個未被訪問過的頂點v3,繼續遍歷,然后訪問v3,鄰接點v6,然后v7。
  • 5.由于v7沒有未被訪問過的鄰接點,所以退回到v6,繼續退回到v3,最后到達v1,發現沒有未被訪問的;
  • 6.結束遍歷。

這里需要引入已訪問的數組Visited,用來標識哪些點被訪問了。因為圖中的元素比時n:m,遍歷的時候如果不引入會出現同一個頂點遍歷多次的情況。

1.2廣度優先搜索

廣度優先搜索的過程類似于樹的廣度優先搜索,首先從例子中體會廣搜,例如圖1是個無向圖,采用廣搜算法遍歷這個圖的過程是:

  • 1.首先任意位置找一個未遍歷過的頂點,例如v1,在看到這個v1后,把這個頂點的所有鄰接點v2,v3,放進隊列中,標記v1的狀態為訪問過;
  • 2.v1訪問后訪問v2,把v2的所有鄰接點且沒有被訪問過的點(v4,v5)入隊,標記v2的狀態為訪問過;
  • 3.v2訪問后訪問v3,把v3的所有鄰接點且沒有被訪問過的點(v6,v7)入隊,標記v3的狀態為訪問過;
  • 4.重復上述過程,直到所有頂點遍歷完;
  • 5.結束遍歷。

值得注意的是這個Visited數組,我們在入隊的時候就可以設置為已經訪問過的元素,我們的處理是訪問出隊的頂點,并把出隊的頂點的鄰接點入隊。

2.鄰接矩陣的代碼實現

鄰接矩陣的頂點結構:

//鄰接矩陣的頂點結構,表示一個頂點的信息
typedef struct{int no;     //頂點編號char *show; //顯示行為(我們不希望打印數字no出來,而是空間中存的數據)
}MatrixVertex;

圖的鄰接矩陣表示法:

typedef int MatrixEdge;
//圖的鄰接矩陣表示法
typedef struct {MatrixVertex vex[MaxNodeNum];               // 存儲頂點信息,使用一維數組存儲頂點MatrixEdge edges[MaxNodeNum][MaxNodeNum];   // 存儲邊的結構,矩陣,數組中存的是權值int nodeNum;                                // 約束實際的頂點數量//<----------上面三個其實就夠了,為了表示任意圖,加了下面兩個變量------------->int edgeNum;                                //邊的數量int directed;                               //是否有向
}MatrixGraph;

因為鄰接矩陣的邊就是個二維矩陣,所以不把它封裝成結構體了,直接構建鄰接矩陣,參照的是《01數據結構-圖的概念和圖的存儲結構》鏈接: https://blog.csdn.net/2302_82136376/article/details/150055797?fromshare=blogdetail&sharetype=blogdetail&sharerId=150055797&sharerefer=PC&sharesource=2302_82136376&sharefrom=from_link中的邏輯。

初始化鄰接矩陣的接口:

void initMatrixGraph(MatrixGraph *graph,const char *names[],int num,int directed,int edgeValue);void addMGraphEdge(MatrixGraph *graph,int x,int y,int w);

先看void initMatrixGraph(MatrixGraph *graph,const char *names[],int num,int directed,int edgeValue);中的參數,第一個傳的是哪一個圖,第二個傳的是一個字符串數組用于初始化MatrixVertex中的char *show,第三個傳的是約束實際的頂點數量,第四個傳的是是否有向,第五個傳的是每條邊的權重,

再看void addMGraphEdge(MatrixGraph *graph,int x,int y,int w);給圖中的兩個頂點添加邊,所以需要MatrixGraph *graph,int x,int y,再給這條邊加權重所以傳int w。

初始化實現:void initMatrixGraph(MatrixGraph *graph,const char *names[],int num,int directed,int edgeValue);

void initMatrixGraph(MatrixGraph *graph, const char *names[], int num, int directed, int edgeValue) {graph->directed=directed;graph->nodeNum=num;graph->edgeNum=0;memset(graph->vex,0,sizeof(graph->vex));memset(graph->edges,0,sizeof(MatrixEdge) * MaxNodeNum * MaxNodeNum);for (int i = 0; i < num; ++i) {graph->vex[i].no=i;graph->vex[i].show=names[i];for (int j = 0; j < num; ++j) {//存權值graph->edges[i][j]=edgeValue;}}
}

先初始化圖中的頂點數量為傳入的頂點數量,將傳入函數的圖中的頂點集和邊集權初始化為0。for循環開始為MatrixVertex中的頂點編號和顯示行為賦值,進入第二重循環,為邊集里的每一條賦權重

添加邊:void addMGraphEdge(MatrixGraph *graph,int x,int y,int w);

static int isEdge(int weight) {if (weight > 0 && weight < INF) {return 1;}return 0;
}void addMGraphEdge(MatrixGraph *graph, int x, int y, int w) {if (x<0 || x>graph->nodeNum) {return;}if (y<0 || y>graph->nodeNum) {return;}if (!isEdge(graph->edges[x][y])) {graph->edges[x][y]=w;//對角線另外一條邊也添加if (graph->directed == 0) {graph->edges[y][x] = w;}graph->edgeNum++;}
}

在static中我們寫了一個判斷圖中兩個頂點之間是否有邊的內在接口函數,采用的方式是權值判斷法,如果傳入的邊的權值大于0并且小于一個極大值,我們就認為這兩個頂點之間有邊,反之則無邊。在void addMGraphEdge(MatrixGraph *graph, int x, int y, int w)函數中假設沒有邊,即isEdge(graph->edges[x][y])返回值為0,我們就連接兩條邊,并賦權重,如果graph->directed == 0我們就認為是個無向圖,由于邊的矩陣是對稱矩陣,我們把對角線另外一條邊也添加。

來小小測試一下;

#include <stdio.h>
#include <stdlib.h>
#include "matrixGraph.h"void testMatrixGraph(MatrixGraph *grap) {char *nodeName[] = {"V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8"};initMatrixGraph(grap, nodeName, sizeof(nodeName) / sizeof(nodeName[0]), 0, 0);addMGraphEdge(grap, 0, 1, 1);addMGraphEdge(grap, 0, 2, 1);addMGraphEdge(grap, 1, 3, 1);addMGraphEdge(grap, 1, 4, 1);addMGraphEdge(grap, 2, 5, 1);addMGraphEdge(grap, 2, 6, 1);addMGraphEdge(grap, 3, 7, 1);addMGraphEdge(grap, 4, 7, 1);addMGraphEdge(grap, 5, 6, 1);
}int main() {MatrixGraph g1;testMatrixGraph(&g1);printf("graph have %d edge!\n", g1.edgeNum);return 0;
}

結果:

D:\work\DataStruct\cmake-build-debug\03_GraphStruct\MatrixGraph.exe
graph have 9 edge!進程已結束,退出代碼為 0

下面來看深度優先遍歷和廣度優先遍歷的代碼

深度優先遍歷:void DFS_MGraph(MatrixGraph *graph,int v);

static int MGraphVisited[MaxNodeNum];void visitMGraphNode(MatrixVertex* node) {printf("%s\t", node->show);
}void DFS_MGraph(MatrixGraph *graph,int v) {visitMGraphNode(&graph->vex[v]);MGraphVisited[v]=1;for (int i = 0; i < graph->nodeNum; ++i) {if (isEdge(graph->edges[v][i])&&MGraphVisited[i]==0) {DFS_MGraph(graph,i);}}
}

寫了一個數組來存已經訪問過的頂點,由于類似于樹的先序遍歷,所以函數進來就要訪問,然后把傳入的頂點設為已被訪問,進入for循環從0號頂點一直遍歷到graph->nodeNum,判斷有哪個頂點與他右邊并且未被訪問,如果找到進入遞歸,一直遞到第一次傳入的頂點v找遍graph->nodeNum這么多頂點,退出遞歸函數。

如圖2我們假設傳入的時0即a這個頂點,訪問a,把a標記為已被訪問(紅色),進入函數的for循環,發現了和1即b頂點間有邊且b還未被訪問,執行DFS_MGraph(graph,i);

第一次遞進去,傳入的是1即b頂點,把b標記為已被訪問(藍色),發現和0即a頂點間有邊但是a已被訪問,繼續遍歷發現和2即c頂點有邊且c頂點未被訪問,執行DFS_MGraph(graph,i);

第二次遞進去,傳入的是2即c頂點,把c標記為已被訪問(綠色),發現和1即b頂點間有邊但是b已被訪問,繼續遍歷發現和3即d頂點有邊且d頂點未被訪問,執行DFS_MGraph(graph,i);

第三次遞進去,傳入的是3即d頂點,把d標記為已被訪問(黃色),發現和0即a頂點間有邊但是b已被訪問,繼續遍歷發現和2即c頂點有邊但是c頂點也已訪問,一直for循環到把5個頂點全部循環完也沒有找到開始歸回去

第一次歸,由于是從第二次遞歸的黃顏色進來的,所以歸回到第二次遞歸的黃顏色格子,在第4個頂點即e有邊且e頂點未被訪問,執行DFS_MGraph(graph,i);

第四次遞進去,此時全部頂點都被訪問,所以開始歸回。

第二次歸,直接歸回到第四行的紫色格子

第三次歸,函數執行完再次歸回到第三行的綠色格子,由于全部頂點都被訪問所以再歸回去。

第四次歸,歸到第二行的藍色格子,全部頂點都被訪問,直接for循環完后退出函數。最終深搜的順序:a->b->c->d->e。

圖2
圖2

廣度優先遍歷:void BFS_MGraph(MatrixGraph *graph,int v);

void initVisited() {memset(MGraphVisited, 0, sizeof(MGraphVisited));
}void BFS_MGraph(MatrixGraph *graph,int v) {int front=0;int rear=0;MatrixVertex *queue[MaxNodeNum];queue[rear]=&graph->vex[v];// 在入隊的同時就標記為已訪問MGraphVisited[v] = 1;rear=(rear+1)%MaxNodeNum;while (front!=rear) {//出隊MatrixVertex *node=queue[front];int nodeNum=node->no;visitMGraphNode(node);front=(front+1)%MaxNodeNum;//入隊for (int i = 0; i < graph->nodeNum; ++i) {if (isEdge(graph->edges[nodeNum][i])&&MGraphVisited[i]==0) {queue[rear]=&graph->vex[i];MGraphVisited[i] = 1;  // 在入隊的同時就標記為已訪問rear=(rear+1)%MaxNodeNum;}}}
}

注意在上面已經給static int MGraphVisited[MaxNodeNum];賦值了所以在開始執行BFS之前,需要初始化MGraphVisited數組。因為MGraphVisited數組中的元素被標記為已訪問(即值為1)

我們在void BFS_MGraph(MatrixGraph *graph,int v) 里面創建一個臨時隊列,初始化這個隊列時就把傳入的v入隊,并在入隊的同時就標記為已訪問,當隊列不為空時,進行出隊入隊的操作,出隊時創建一個臨時頂點變量node,讓它拿到node的no(因為我們在for循環里需要拿到出隊的元素不斷判斷與其他頂點是否有邊,若有邊就入隊)并訪問它,在for循環里用拿到的nodeNum遍歷graph->vex[nodeNum]和其他頂點是否有邊,其他沒有被訪問的頂點,如果找到滿足條件的頂點,入隊,并在入隊的同時就標記為已訪問。

如圖3,我們假設傳入的時0即a這個頂點,將a入隊,再出隊,出隊的時候直接把5個頂點全部遍歷完,發現1即b頂點和3即d頂點有邊且未被訪問,直接把b和d頂點入隊

把先進來的b出隊,出隊的時候直接把5個頂點全部遍歷完,發現0即a頂點,2即c頂點和4即e頂點有邊但是a頂點已被訪問剩下兩個沒有,直接把c和e頂點入隊

把d出隊,發現1即b頂點和2即c頂點有邊但已被訪問直接開始出隊

重復上述過程直到隊列為空,最終廣搜的順序:a->b->d->c->e。

圖3
圖3

大概先寫這些吧,今天的博客就先寫到這,謝謝您的觀看。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94925.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94925.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94925.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenAI發布的GPT-5 更新了哪些內容,它的核心能力有哪些?AI編碼能力這么強,前端程序員何去何從?

目錄**1. GPT-5的核心能力與技術突破****1.1 智能水平的質變****1.2 代碼生成與優化****1.3 上下文處理與長文本能力****1.4 安全與可靠性改進****2. GPT-5的應用場景與案例****2.1 醫療領域****2.2 教育與學習****2.3 企業級應用****2.4 軟件開發****3. 技術細節與創新****3.1…

【無標題】AI 賦能日常效率:實用案例與操作心得分享

大語言模型&#xff08;LLM&#xff09;早已不再是實驗室里的專屬品&#xff0c;而是逐漸滲透到我們工作與生活的方方面面。從繁瑣的文檔處理到復雜的信息篩選&#xff0c;從學習輔助到日常規劃&#xff0c;AI 正以 "微生產力" 的形式重塑我們的效率邊界。本文將分享…

Java-線程線程的創建方式

一.進程和線程進程&#xff1a;進程是資源分配的基本單位&#xff0c;每個進程都有自己獨立的內存空間&#xff0c;可以看作是一個正在運行的程序實例線程&#xff1a;線程是CPU調度的基本單位&#xff0c;屬于進程&#xff0c;一個進程可以包含多個線程。線程共享進程的內存空…

Electron 中 license-keys 的完整集成方案

secure-electron-license-keys 是一個專門為 Electron 應用設計的 npm 包&#xff0c;用于實現離線許可證密鑰的創建、驗證和管理&#xff0c;幫助開發者保護應用程序&#xff0c;確保只有擁有合法許可證的用戶才能使用。以下是關于它的詳細介紹&#xff1a; 在 Electron 應用中…

AI推理的“靈魂五問”:直面2025算力鴻溝與中國的破局之路

摘要&#xff1a;2025年&#xff0c;AI產業的重心已從訓練全面轉向推理&#xff0c;但一場嚴峻的“體驗”危機正悄然上演。中美AI推理性能的巨大鴻溝&#xff0c;正讓國內廠商面臨用戶流失的切膚之痛。本文以問答形式&#xff0c;直面當前中國AI產業在推理“最后一公里”上最尖…

2025 TexLive+VScode排版IEEE TGRS論文

2025 TexLiveVScode排版IEEE TGRS論文 本文主要內容&#xff1a; 軟件安裝 latex 排版 TRGS 論文期間遇到的問題 清晰圖片導出 Latex公式、圖、表、算法、參考文獻的使用和引用 1. 前言 首先使用Overleaf網頁版排版&#xff0c;但是后期排版圖片太大&#xff0c;大小有限制&…

Redis數據組織方式

前言 Redis之所以高效&#xff0c;源自其優秀的架構設計。作為KV鍵值對存儲數據庫&#xff0c;數據的存儲放在了內存中&#xff0c;KV鍵值對的組織方式更是其高效的原因之一。本文介紹其數據組織方式。 一、總體架構 在使用Redis時&#xff0c;服務端接收多個客戶端的命令進行…

java組件安全vulhub靶場

>1--XStream1.打開靶場cd vulhub-master/xstream/CVE-2021-29505 docker up -d2.下載反序列化工具https://github.com/frohoff/ysoserial可以使用clone命令進行下載&#xff0c;也可以直接下載jar文件3.使用以下命令來開啟腳本&#xff0c;將是反彈shell的語句進行base64編碼…

UCMT部分復現

復現結果&#xff1a;88.03272&#xff0c;誤差在接受范圍內 補充信息 作者未解決后續報錯問題&#xff0c;不建議復現

IntelliJ IDEA 新手全方位使用指南

摘要本文面向剛接觸軟件開發、使用 IntelliJ IDEA 的新手&#xff0c;詳細介紹了 IDEA 的背景、版本區別、核心功能、運行原理、界面操作、項目管理、運行配置、以及 Git 版本控制基礎。文章突出實用操作和理解流程&#xff0c;幫助新手快速熟悉IDEA環境&#xff0c;順利完成項…

Python如何將圖片轉換為PDF格式

引言 在日常工作和學習中&#xff0c;我們經常需要將多張圖片合并成一個PDF文件&#xff0c;以便于分享或打印。Python提供了多種庫來實現這一需求&#xff0c;本文將詳細介紹三種常用的方法&#xff1a;img2pdf庫、Pillow庫和PyMuPDF庫&#xff0c;并附上完整的代碼示例。 方法…

Python如何合并兩個Excel文件

引言 在日常數據處理中&#xff0c;合并Excel文件是常見需求。Python提供了多種庫&#xff08;如pandas、openpyxl&#xff09;來實現這一操作。本文將詳細介紹兩種主流方法&#xff0c;并附上完整代碼示例&#xff0c;幫助您高效完成Excel合并任務。 方法一&#xff1a;使用pa…

【SQL進階】用EXPLAIN看透SQL執行計劃:從“盲寫“到“精準優化“

用EXPLAIN洞察SQL執行計劃&#xff1a;從"盲目編寫"到"精準優化" 很多開發者在編寫SQL時僅憑直覺&#xff0c;直到查詢超時才發現問題。MySQL內置的EXPLAIN工具能提前揭示查詢執行邏輯&#xff0c;幫助預防性能隱患。本文將帶你掌握EXPLAIN的核心用法&…

電影藝術好,電影知識得學

關于電影應該談什么導演風格、演員技術、劇本結構、票房、政治因素等。一、紙上談電影電影制作期&#xff1a;研發、前制、拍攝、后制、發行。一般成員只在某個時期出現。制片和導演會從頭監督到尾。研發期&#xff1a; 劇本概念發想與成形的時期。創作自由度比較大&#xff0c…

FPGA學習筆記——簡易的DDS信號發生器

目錄 一、任務 二、分析 三、ROM IP核配置 四、Visio圖 五、代碼 &#xff08;1&#xff09;.v代碼 &#xff08;2&#xff09;仿真代碼 六、仿真 七、實驗現象 一、任務 用串口模塊&#xff0c;用上位機發送指令&#xff0c;FPGA接收&#xff0c;然后輸出對應的波形&…

在NVIDIA Orin上用TensorRT對YOLO12進行多路加速并行推理時內存泄漏 (中)

接上篇 在NVIDIA Orin上用TensorRT對YOLO12進行多路加速并行推理時內存泄漏&#xff08;上&#xff09; 通過上篇的分析&#xff0c;發現問題在采集數據到傳入GPU之前的階段。但隨著新一輪長時間測試發現&#xff0c;問題依然存在。 如上圖&#xff0c;在運行20多分鐘內存開始…

計數組合學7.17(Murnaghan–Nakayama 規則 )

7.17 Murnaghan–Nakayama 規則 我們已經成功地用基 mλm_\lambdamλ?、hλh_\lambdahλ? 和 eλe_\lambdaeλ? 表示了 Schur 函數 sλs_\lambdasλ?。本節我們將考慮冪和對稱函數 pλp_\lambdapλ?。一個斜分劃 λ/μ\lambda / \muλ/μ 是連通的&#xff0c;如果其分拆圖…

使用 jlink 構建輕巧的自定義JRE

從 JDK 9 開始&#xff0c;Oracle JDK 和 OpenJDK 不再默認包含獨立的 JRE 目錄&#xff0c;而是提供了 jlink 工具&#xff08;Java 鏈接器&#xff09;&#xff0c;允許你根據需求自定義生成最小化的 JRE&#xff08;包含必要的模塊&#xff09;。以下是使用 jlink 生成 JRE …

[IOMMU]面向芯片/SoC驗證工程的IOMMU全景速覽

面向芯片/SoC驗證工程的IOMMU全景速覽 摘要:面向芯片/SoC 驗證工程的 IOMMU 全景速覽:包含基礎概念、主流架構要點(ARM SMMU、Intel VT?d、RISC?V IOMMU),Linux 軟件棧關系,SoC 上的驗證方法(功能、錯誤、性能、系統化流程和覆蓋),以及一個可用的“通用 IOMM…

Jenkins全鏈路教程——Jenkins用戶權限矩陣配置

在企業級CI/CD場景中&#xff0c;“權限混亂”往往比“構建失敗”更致命——測試員誤刪生產流水線、實習生修改關鍵插件配置、多團隊共用賬號導致責任無法追溯……這些問題&#xff0c;99%都能用權限矩陣徹底解決&#xff01;今天&#xff0c;我們不僅會拆解權限矩陣的底層邏輯…