26考研——圖_圖的代碼實操(6)

408答疑


文章目錄

  • 五、圖的代碼實操
    • 圖的存儲
      • 鄰接矩陣
        • 結構定義
        • 初始化
        • 插入頂點
        • 獲取頂點位置
        • 在頂點 v1 和 v2 之間插入邊
        • 獲取第一個鄰接頂點
        • 獲取下一個鄰接頂點
        • 顯示圖
      • 鄰接表
        • 結構定義
        • 初始化圖
        • 插入頂點
        • 獲取頂點位置
        • 在頂點 v1 和 v2 之間插入邊
        • 獲取第一個鄰接頂點
        • 獲取下一個鄰接頂點
        • 顯示圖
    • 圖的遍歷
      • 深度優先遍歷
        • 算法思想
          • 算法步驟
          • 算法特點
        • 鄰接矩陣
        • 鄰接表
      • 廣度優先遍歷
        • 算法思想
        • 鄰接矩陣
        • 鄰接表
    • 圖的應用
      • BFS算法求解單源最短路徑問題
      • 拓撲排序
  • 六、參考資料
    • 鮑魚科技課件
    • 26王道考研書


五、圖的代碼實操

圖的存儲

鄰接矩陣

結構定義
typedef struct GraphMtx {int numV; // 頂點個數int numE; // 邊的條數ElemType vList[MAX_VERTEX_SIZE]; // 頂點空間int edge[MAX_VERTEX_SIZE][MAX_VERTEX_SIZE]; // 邊的矩陣
} GraphMtx;
初始化
  • 初始化圖的頂點數和邊數為 0,并將鄰接矩陣的所有元素設置為 0。
void initGraph(GraphMtx *g) {g->numV = 0;g->numE = 0;for (int i = 0; i < MAX_VERTEX_SIZE; ++i) {for (int j = 0; j < MAX_VERTEX_SIZE; ++j)g->edge[i][j] = 0;}
}
插入頂點
  • 在圖中插入一個新頂點,如果頂點數超過最大值則不插入。
void insertVertex(GraphMtx *g, ElemType vertex) {if (g->numV >= MAX_VERTEX_SIZE)return;g->vList[g->numV] = vertex;g->numV++;
}
獲取頂點位置
  • 返回頂點在頂點列表中的位置,如果不存在則返回 -1。
int getPosVertex(GraphMtx *g, ElemType vertex) {for (int i = 0; i < g->numV; ++i) {if (g->vList[i] == vertex)return i;}return -1; // 沒有要查找的頂點
}
在頂點 v1 和 v2 之間插入邊
  • 在圖中插入一條從頂點 vertex1 到頂點 vertex2 的邊。
void insertEdge(GraphMtx *g, ElemType vertex1, ElemType vertex2) {int v1 = getPosVertex(g, vertex1);int v2 = getPosVertex(g, vertex2);// 插入v1->v2邊g->edge[v1][v2] = 1;// 若是無向圖,則插入v2->v1邊//g->edge[v2][v1] = 1;g->numE++;
}
獲取第一個鄰接頂點
  • 獲取給定頂點的第一個鄰接頂點。
int getFirstNeighbor(GraphMtx *g, ElemType vertex) {int v = getPosVertex(g, vertex);if (v == -1)return -1; // 沒有鄰接頂點for (int j = 0; j < g->numV; ++j) {if (g->edge[v][j] == 1)return j; // 返回鄰接頂點}return -1; // 沒有鄰接頂點
}
獲取下一個鄰接頂點
  • 獲取給定頂點的下一個鄰接頂點。
int getNextNeighbor(GraphMtx *g, ElemType vertex1, ElemType vertex2) {int v1 = getPosVertex(g, vertex1);int v2 = getPosVertex(g, vertex2);if (v1 == -1 || v2 == -1)return -1;for (int j = v2 + 1; j < g->numV; ++j) {if (g->edge[v1][j] == 1)return j;}return -1;
}
顯示圖
  • 顯示圖的鄰接矩陣。
void showGraph(GraphMtx *g) {printf(" ");for (int i = 0; i < g->numV; ++i)printf(" %c", g->vList[i]);printf("\n");for (int i = 0; i < g->numV; ++i) {printf("%c ", g->vList[i]);for (int j = 0; j < g->numV; ++j) {printf("%d ", g->edge[i][j]);}printf("\n");}
}

鄰接表

結構定義
typedef struct Edge {int dest; // 目標頂點的下標struct Edge *next; // 結點指針
} Edge;typedef struct Vertex {ElemType data;   // 頂點數據Edge *first; // 指向邊的起始指針
} Vertex;typedef struct GraphLnk {int numV; // 頂點數int numE; // 邊的條數Vertex nodeTable[MAX_VERTEX_SIZE];
} GraphLnk;
初始化圖
  • 初始化圖的頂點數和邊數為 0,并將鄰接表的所有元素設置為 NULL。
void initGraph(GraphLnk *g) {g->numV = 0;g->numE = 0;for (int i = 0; i < MAX_VERTEX_SIZE; ++i)g->nodeTable[i].first = NULL;
}
插入頂點
  • 在圖中插入一個新頂點,如果頂點數超過最大值則不插入。
void insertVertex(GraphLnk *g, ElemType vertex) {if (g->numV >= MAX_VERTEX_SIZE)return;g->nodeTable[g->numV].data = vertex;g->numV++;
}
獲取頂點位置
  • 返回頂點在頂點列表中的位置,如果不存在則返回 -1。
int getPosVertex(GraphLnk *g, ElemType vertex) {for (int i = 0; i < g->numV; ++i) {if (g->nodeTable[i].data == vertex)return i;}return -1; // 沒有要查找的頂點
}
在頂點 v1 和 v2 之間插入邊
  • 在圖中插入一條從頂點 vertex1 到頂點 vertex2 的邊。
void insertEdge(GraphLnk *g, ElemType vertex1, ElemType vertex2) {int v1 = getPosVertex(g, vertex1);int v2 = getPosVertex(g, vertex2);// v1->v2Edge *e = (Edge*)malloc(sizeof(Edge));e->dest = v2;e->next = g->nodeTable[v1].first;g->nodeTable[v1].first = e;// 	若是無向圖,則插入v2->v1邊//e = (Edge*)malloc(sizeof(Edge));//e->dest = v1;//e->next = g->nodeTable[v2].first;//g->nodeTable[v2].first = e;g->numE++;
}
獲取第一個鄰接頂點
  • 獲取給定頂點的第一個鄰接頂點。
int getFirstNeighbor(GraphLnk *g, ElemType vertex) {int v = getPosVertex(g, vertex);if (v == -1)return -1; // 沒有鄰接頂點if (g->nodeTable[v].first != NULL)return g->nodeTable[v].first->dest;return -1; // 沒有鄰接頂點
}
獲取下一個鄰接頂點
  • 獲取給定頂點的下一個鄰接頂點。
int getNextNeighbor(GraphLnk *g, ElemType vertex1, ElemType vertex2) {int v1 = getPosVertex(g, vertex1);int v2 = getPosVertex(g, vertex2);if (v1 == -1 || v2 == -1)return -1;Edge *p = g->nodeTable[v1].first;while (p->dest != v2)p = p->next;p = p->next;if (p != NULL)return p->dest;return -1;
}
顯示圖
  • 顯示圖的鄰接表。
void showGraph(GraphLnk *g) {for (int i = 0; i < g->numV; ++i) {printf("%d %c : ", i, g->nodeTable[i].data);Edge *p = g->nodeTable[i].first;while (p != NULL) {printf("%d->", p->dest);p = p->next;}printf("Nil.\n");}
}

圖的遍歷

深度優先遍歷

算法思想

深度優先遍歷(DFS)是一種用于遍歷或搜索圖或樹的算法。該算法使用棧(顯式或遞歸)來實現非遞歸約的遍歷過程。算法從圖中的某一頂點開始,沿著路徑深入訪問盡可能遠的頂點,直到不能再深入為止,然后回溯并訪問其他路徑。

算法步驟
  1. 訪問起始頂點:選擇圖中的某一頂點作為起始點,并標記為已訪問。
  2. 訪問鄰接頂點:訪問該頂點的所有未被訪問的鄰接頂點,對每個鄰接頂點遞歸地調用DFS。
  3. 回溯:當當前路徑無法繼續深入時,回溯到最近的已訪問頂點,檢查是否有其他未訪問的鄰接頂點。
  4. 重復過程:重復上述過程,直到圖中所有頂點都被訪問。
算法特點
  • DFS 相當于二叉樹中的前序遍歷。
  • 使用臨時空間來標記結點是否被訪問過。
  • 適用于圖的連通性檢測、拓撲排序等場景。
鄰接矩陣
void DFS(GraphMtx *g, ElemType vertex) {printf("%c->", vertex);int v = getPosVertex(g, vertex);visit[v] = 1; // 標記頂點int w = getFirstNeighbor(g, vertex);while (w != -1) {if (!visit[w]) {DFS(g, g->vList[w]);}w = getNextNeighbor(g, g->vList[v], g->vList[w]); //(g, v1, v2)}
}
鄰接表
void DFS(GraphLnk *g, ElemType vertex) {printf("%c->", vertex);int v = getPosVertex(g, vertex);visit[v] = 1; // 標記頂點int w = getFirstNeighbor(g, vertex);while (w != -1) {if (!visit[w]) {DFS(g, g->nodeTable[w].data);}w = getNextNeighbor(g, g->nodeTable[v].data, g->nodeTable[w].data); //(g, v1, v2)}
}

廣度優先遍歷

算法思想
  1. 初始化

    • 標記起始頂點為已訪問。
    • 將起始頂點入隊。
  2. 遍歷隊列:當隊列不為空時,執行以下操作。

    • 從隊列中取出一個頂點。
    • 訪問該頂點的所有未訪問過的鄰接頂點。
    • 將這些鄰接頂點標記為已訪問,并加入隊列。
  3. 重復步驟2,直到隊列為空,即所有可達頂點都被訪問。

鄰接矩陣
void BFS(GraphMtx *g, ElemType vertex) {printf("%c->", vertex);int v = getPosVertex(g, vertex);visit[v] = 1;int Q[MAX_VERTEX_SIZE];int front = 0, rear = 0;// 入隊Q[rear++] = v;while (front != rear) { // 隊列不空v = Q[front++]; // 出隊并保存頂點int w = getFirstNeighbor(g, g->vList[v]);while (w != -1) {if (!visit[w]) {printf("%c->", g->vList[w]);visit[w] = 1;Q[rear++] = w;}w = getNextNeighbor(g, g->vList[v], g->vList[w]);}}
}
鄰接表
void BFS(GraphLnk *g, ElemType vertex) {printf("%c->", vertex);int v = getPosVertex(g, vertex);visit[v] = 1;int Q[MAX_VERTEX_SIZE];int front = 0, rear = 0;// 入隊Q[rear++] = v;while (front != rear) { // 隊列不空v = Q[front++]; // 出隊并保存頂點int w = getFirstNeighbor(g, g->nodeTable[v].data);while (w != -1) {if (!visit[w]) {printf("%c->", g->nodeTable[w].data);visit[w] = 1;Q[rear++] = w;}w = getNextNeighbor(g, g->nodeTable[v].data, g->nodeTable[w].data);}}
}

圖的應用

BFS算法求解單源最短路徑問題

  1. 初始化

    • 設置所有頂點的最短路徑長度為無窮大,起始頂點的距離為0。
    • 標記起始頂點為已訪問。
    • 將起始頂點加入隊列。
  2. BFS遍歷:當隊列非空時,執行以下操作。

    • 從隊列中取出一個頂點。
    • 遍歷該頂點的所有未訪問鄰接頂點。
    • 更新鄰接頂點的最短路徑長度。
    • 標記鄰接頂點為已訪問。
    • 將鄰接頂點加入隊列。
  3. 完成遍歷

    • 重復步驟2,直到隊列為空。
    • 此時,數組d中存儲的即為從起始頂點到所有可達頂點的最短路徑長度。
void BFS_MIN_Distance(GraphMtx G, int u) {// d[i]表示從u到i結點的最短路徑長度int d[MAX_VERTEX_SIZE]; int visited[MAX_VERTEX_SIZE]; // 訪問標記數組Queue Q; // 定義隊列Qfor (int i = 0; i < G.numV; ++i) {d[i] =; // 初始化路徑長度為無窮大visited[i] = FALSE; // 初始化所有頂點為未訪問}d[u] = 0; // 起始頂點到自身的距離為0visited[u] = TRUE; // 標記起始頂點為已訪問EnQueue(Q, u); // 將起始頂點入隊while (!IsEmpty(Q)) { // BFS算法主過程int v;DeQueue(Q, v); // 隊頭元素v出隊for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) {if (!visited[w]) { // w為v的尚未訪問的鄰接頂點visited[w] = TRUE; // 標記為已訪問d[w] = d[v] + G.edge[v][w]; // 路徑長度加v到w的邊的權重EnQueue(Q, w); // 頂點w入隊}}}
}

拓撲排序

  • 使用棧實現圖的拓撲排序,輸出頂點的拓撲序列。
void topSort(GraphMtx *g) {// 統計每一個頂點的入度for (int j = 0; j < g->numV; ++j) {for (int i = 0; i < g->numV; ++i) {if (g->edge[i][j] == 1)inDegree[j]++;}}// 定義一個棧ElemType stack[MAX_VERTEX_SIZE] = { 0 };int top = 0;// 把入度為0的頂點入棧for (int i = 0; i < g->numV; ++i) {if (inDegree[i] == 0) // 入度為0stack[top++] = i;}for (int i = 0; i < g->numV; ++i) { // 排序輸出每一個頂點if (top == 0) {printf("圖有回路.\n");return;}int v = stack[--top];printf("%c->", g->vList[v]);int w = getFirstNeighbor(g, g->vList[v]);while (w != -1) {if (--inDegree[w] == 0)stack[top++] = w;w = getNextNeighbor(g, g->vList[v], g->vList[w]);}}
}

六、參考資料

鮑魚科技課件

b站免費王道課后題講解:
在這里插入圖片描述

網課全程班:
在這里插入圖片描述

26王道考研書

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

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

相關文章

開源webmail郵箱客戶端rainloop的分支版本SnappyMail 設置發件人允許多重身份

RainLoop已多年未更新&#xff0c;SnappyMail 是 RainLoop 的分支&#xff0c;由社區維護。SnappyMail 不僅修復了漏洞&#xff0c;還增加了更多功能和優化。對 IMAP 支持更好&#xff0c;移動端體驗也比 RainLoop 更細致。 安裝過程和設置跟RainLoop一樣&#xff1a; 以寶塔面…

海量數據場景題--查找兩個大文件的URL

查找兩個大文件共同的URL 給定 a、b 兩個文件&#xff0c;各存放 50 億個 URL&#xff0c;每個 URL 各占 64B&#xff0c;找出 a、b 兩個文件共同的 URL。內存限制是 4G。 操作邏輯&#xff1a; 使用哈希函數 hash(URL) % 1000? 將每個URL映射到0-999的編號 文件A切割為a0, a1…

簡單ELK框架搭建

簡介 ELK 框架是一套開源的日志管理和分析工具&#xff0c;由 Elasticsearch、Logstash 和 Kibana 三個主要組件組成&#xff0c;現在新增了Filebeat組件&#xff0c;可以更高效的收集數據。 Elasticsearch&#xff1a;是一個分布式、高可擴展的開源搜索引擎&#xff0c;能快速…

VS Code 中 .history`文件的來源與 .gitignore`的正確使用

引言 在使用 VS Code 進行 Git 版本控制時&#xff0c;有時會發現項目中多出一個 .history 目錄&#xff0c;并被 Git 識別為未跟蹤文件。本文將解釋 .history 的來源&#xff0c;并提供 .gitignore 的正確配置方法&#xff0c;確保開發環境的整潔性。 1. .history 文件的來源…

網絡之數據鏈路層

數據鏈路層 數據鏈路層目標 TCP/IP提供了一種能力, 將數據可靠的從 B 跨網絡送到 C 主機, 這期間是由無數次局域網轉發構成的, 比如 主機B 到 路由器F 就是一次局域網通信的問題, 而數據鏈路層就是研究數據是如何在局域網內部轉發的. 也就是說, 應用層是進行數據的處理, 傳輸…

A Brief History: from GPT-1 to GPT-3

This is my reading notes of 《Developing Apps with GPT-4 and ChatGPT》. In this section, we will introduce the evolution of the OpenAI GPT medels from GPT-1 to GPT-4. GPT-1 In mid-2018, OpenAI published a paper titled “Improving Language Understanding …

基于大數據的各品牌手機銷量數據可視化分析系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 時代在飛速進步&#xff0c;每個行業都在努力發展現在先進技術&#xff0c;通過這些先進的技術來提高自己的水平和優勢&#xff0c;各品牌手機銷量數據可視化分析系統當然不能排除在外。基于大數據的各品牌手機銷量數據可視化分析系統是在實際應用和軟件工程的開發原理之…

人工智能-群暉Docker部署DB-GPT

人工智能-群暉Docker部署DB-GPT 0 環境及說明1 獲取dbgpt的docker鏡像2 下載向量模型3 下載配置文件4 修改配置文件5 創建dbgpt容器并運行6 訪問dbgpt0 環境及說明 環境項說明DSM版本DSM 7.2.1-69057 update 3Container Manager版本24.0.2-1535當前 hub.docker.com 鏡像倉庫中的…

Netty——TCP 粘包/拆包問題

文章目錄 1. 什么是 粘包/拆包 問題&#xff1f;2. 原因2.1 Nagle 算法2.2 滑動窗口2.3 MSS 限制2.4 粘包的原因2.5 拆包的原因 3. 解決方案3.1 固定長度消息3.2 分隔符標識3.3 長度前綴協議3.3.1 案例一3.3.2 案例二3.3.3 案例三 4. 總結 1. 什么是 粘包/拆包 問題&#xff1f…

JavaScript Fetch API

簡介 fetch() API 是用于發送 HTTP 請求的現代異步方法&#xff0c;它基于 Promise&#xff0c;比傳統的 XMLHttpRequest 更加簡潔、強大 示例 基本語法 fetch(url, options).then(response > response.json()).then(data > console.log(data)).catch(error > con…

UMI-OCR Docker 部署

額外補充 Docker 0.前置條件 部署前&#xff0c;請檢查主機的CPU是否具有AVX指令集 lscpu | grep avx 輸出如下即可繼續部署 Flags: ... avx ... avx2 ... 1.下載dockerfile wget https://raw.githubusercontent.com/hiroi-sora/Umi-OCR_runtime_linux/main/Do…

C++ --- 二叉搜索樹

1 二叉搜索樹的概念 ?叉搜索樹?稱?叉排序樹&#xff0c;它或者是?棵空樹&#xff0c;或者是具有以下性質的?叉樹: 1 若它的左?樹不為空&#xff0c;則左?樹上所有結點的值都?于等于根結點的值 2 若它的右?樹不為空&#xff0c;則右?樹上所有結點的值都?于等于根結點…

跨語言語言模型預訓練

摘要 最近的研究表明&#xff0c;生成式預訓練在英語自然語言理解任務中表現出較高的效率。在本研究中&#xff0c;我們將這一方法擴展到多種語言&#xff0c;并展示跨語言預訓練的有效性。我們提出了兩種學習跨語言語言模型&#xff08;XLM&#xff09;的方法&#xff1a;一種…

文件描述符,它在哪里存的,exec()后還存在嗎

學過計系肯定了解 寄存器、程序計數器、堆棧這些 程序運行需要的資源。 這些是進程地址空間。 而操作系統分配一個進程資源時&#xff0c;分配的是 PCB 進程控制塊。 所以進程控制塊還維護其他資源——程序與外部交互的資源——文件、管道、套接字。 文章目錄 文件描述符進程管…

Slidev使用(一)安裝

文章目錄 1. **安裝位置**2. **使用方式**3. **適用場景**4. **管理和維護** 全局安裝1. **檢查 Node.js 和 npm 是否已安裝**2. **全局安裝 Slidev CLI**3. **驗證安裝是否成功**4. **創建幻燈片文件**5. **啟動 Slidev**6. **實時編輯和預覽**7. **構建和導出&#xff08;可選…

第二十一章:模板與繼承_《C++ Templates》notes

模板與繼承 重點和難點編譯與測試說明第一部分&#xff1a;多選題 (10題)第二部分&#xff1a;設計題 (5題)答案與詳解多選題答案&#xff1a;設計題參考答案 測試說明 重點和難點 21.1 空基類優化&#xff08;EBCO&#xff09; 知識點 空基類優化&#xff08;Empty Base Cla…

AOA與TOA混合定位,MATLAB例程,自適應基站數量,三維空間下的運動軌跡,濾波使用EKF

本代碼實現了一個基于 到達角(AOA) 和 到達時間(TOA) 的混合定位算法,結合 擴展卡爾曼濾波(EKF) 對三維運動目標的軌跡進行濾波優化。代碼通過模擬動態目標與基站網絡,展示了從信號測量、定位解算到軌跡濾波的全流程,適用于城市峽谷、室內等復雜環境下的定位研究。 文…

量子計算:開啟未來計算的新紀元

一、引言 在當今數字化時代&#xff0c;計算技術的飛速發展深刻地改變了我們的生活和工作方式。從傳統的電子計算機到如今的高性能超級計算機&#xff0c;人類在計算能力上取得了巨大的進步。然而&#xff0c;隨著科技的不斷推進&#xff0c;我們面臨著越來越多的復雜問題&…

AMD機密計算虛擬機介紹

一、什么機密計算虛擬機 機密計算虛擬機 是一種基于硬件安全技術(如 AMD Secure Encrypted Virtualization, SEV)的虛擬化環境,旨在保護虛擬機(VM)的 ?運行中數據?(包括內存、CPU 寄存器等)免受外部攻擊或未經授權的訪問,即使云服務提供商或管理員也無法窺探。 AMD …

如何通過數據可視化提升管理效率

通過數據可視化提升管理效率的核心方法包括清晰展示關鍵指標、及時發現和解決問題、支持決策優化。其中&#xff0c;清晰展示關鍵指標尤為重要。通過數據可視化工具直觀地呈現關鍵績效指標&#xff08;KPI&#xff09;&#xff0c;管理者能快速、準確地理解業務現狀&#xff0c…