算法思想 之 拓撲排序問題

歡迎拜訪:霧里看山-CSDN博客
本篇主題算法思想 之 拓撲排序問題
發布時間:2025.8.4
隸屬專欄:算法

在這里插入圖片描述

目錄

  • 算法介紹
    • 核心原理
    • 適用場景
    • 實現步驟(Kahn 算法)
  • 例題
    • 課程表
      • 題目鏈接
      • 題目描述
      • 算法思路
      • 代碼實現
    • 課程表 II
      • 題目鏈接
      • 題目描述
      • 算法思路
      • 代碼實現
    • 火星詞典
      • 題目鏈接
      • 題目描述
      • 算法思路
      • 代碼實現

算法介紹

拓撲排序(Topological Sorting)是針對有向無環圖(DAG, Directed Acyclic Graph) 的一種重要排序算法,其核心是將圖中所有頂點按照一定規則排列,使得對于圖中任意一條有向邊 (u, v),頂點 u 都排在頂點 v 之前。

拓撲排序的本質是解決 “依賴關系” 問題—— 當任務 / 節點之間存在先后依賴(如 “必須完成 A 才能做 B”)時,通過拓撲排序可以找到一個合法的執行順序;若圖中存在環(如 “A 依賴 B,B 依賴 C,C 依賴 A”),則不存在拓撲排序(此時可用于檢測環)。

核心原理

拓撲排序的關鍵是 “消除入度為 0 的節點”

  • 入度(In-degree):頂點被指向的邊的數量(即 “前驅節點” 的數量)。
  • 入度為 0 的節點:沒有前驅依賴,可優先執行 / 輸出。
  • 流程:反復找到入度為 0 的節點,輸出該節點后移除其所有出邊(減少其鄰接節點的入度),直至所有節點被輸出(合法拓撲序)或無法找到入度為 0 的節點(存在環,無拓撲序)。

適用場景

拓撲排序廣泛應用于 “存在依賴關系” 的場景:

  • 課程安排:修課需先完成先修課程(如 “修算法前必須修數據結構”)。
  • 任務調度:任務 A 必須在任務 B 之前執行。
  • 編譯依賴:程序模塊的編譯順序(被依賴的模塊先編譯)。
  • 依賴包安裝:軟件包的安裝順序(依賴的包先安裝)。

實現步驟(Kahn 算法)

Kahn 算法是最直觀的拓撲排序實現,利用隊列存儲 “入度為 0 的節點”,通過層次遍歷逐步消除依賴。

步驟

  • 構建鄰接表:存儲圖的邊關系(u -> v 表示 uv 的前驅)。
  • 計算入度數組:記錄每個節點的入度(初始值)。
  • 初始化隊列:將所有入度為 0 的節點加入隊列(這些節點無依賴,可優先輸出)。
  • BFS 遍歷:
    • 從隊列中取出一個節點 u,加入拓撲序結果。
    • 遍歷 u 的所有鄰接節點 v,將 v 的入度減 1(因為 u 已被 “消除”,v 的一個依賴 被滿足)。
    • v 的入度減為 0,將其加入隊列(此時 v 已無依賴)。
  • 檢查結果:若拓撲序的長度等于節點總數,說明是合法 DAG(存在拓撲序);否則圖中存在環(無拓撲序)。

例題

課程表

題目鏈接

207. 課程表

題目描述

你這個學期必須選修 numCourses 門課程,記為 0numCourses - 1

在選修某些課程之前需要一些先修課程。 先修課程按數組 prerequisites 給出,其中 prerequisites[i] = [ai, bi] ,表示如果要學習課程 ai 則 必須 先學習課程 bi

  • 例如,先修課程對 [0, 1] 表示:想要學習課程 0 ,你需要先完成課程 1


請你判斷是否可能完成所有課程的學習?如果可以,返回 true ;否則,返回 false

示例 1

輸入:numCourses = 2, prerequisites = [[1,0]]
輸出:true
解釋:總共有 2 門課程。學習課程 1 之前,你需要完成課程 0 。這是可能的。

示例 2

輸入:numCourses = 2, prerequisites = [[1,0],[0,1]]
輸出:false
解釋:總共有 2 門課程。學習課程 1 之前,你需要先完成?課程 0 ;并且學習課程 0 之前,你還應先完成課程 1 。這是不可能的。

提示

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= 5000
  • prerequisites[i].length == 2
  • 0 <= ai, bi < numCourses
  • prerequisites[i] 中的所有課程對 互不相同

算法思路

標準拓撲排序問題,直接使用算法介紹中的實現步驟進行即可。

代碼實現

class Solution {
public:bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {//1. 準備工作unordered_map<int, vector<int>> edges; // 鄰接表存圖vector<int> in(numCourses);// 保存每一個點的入度//2. 建圖for(auto& e : prerequisites){int a = e[0], b = e[1];// b->aedges[b].push_back(a);in[a]++;}//3. 拓撲排序queue<int> q;// (1). 把所有入度為0的點存進去for(int i = 0; i < numCourses; i++){if(in[i] == 0)q.push(i);}// (2). bfswhile(q.size()){int n = q.front();q.pop();for(int a : edges[n]){in[a]--;if(in[a] == 0)q.push(a);}}// (3). 判斷是否有環for(int i : in){if(i)return false;}return true;}
};

在這里插入圖片描述

課程表 II

題目鏈接

210. 課程表 II

題目描述

現在你總共有 numCourses 門課需要選,記為 0numCourses - 1。給你一個數組 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在選修課程 ai必須 先選修 bi

  • 例如,想要學習課程 0 ,你需要先完成課程 1 ,我們用一個匹配來表示:[0,1]


返回你為了學完所有課程所安排的學習順序。可能會有多個正確的順序,你只要返回 任意一種 就可以了。如果不可能完成所有課程,返回 一個空數組

示例 1

輸入:numCourses = 2, prerequisites = [[1,0]]
輸出:[0,1]
解釋:總共有 2 門課程。要學習課程 1,你需要先完成課程 0。因此,正確的課程順序為 [0,1] 。

示例 2

輸入:numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
輸出:[0,2,1,3]
解釋:總共有 4 門課程。要學習課程 3,你應該先完成課程 1 和課程 2。并且課程 1 和課程 2 都應該排在課程 0 之后。
因此,一個正確的課程順序是 [0,1,2,3] 。另一個正確的排序是 [0,2,1,3] 。

示例 3

輸入:numCourses = 1, prerequisites = []
輸出:[0]

提示

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= numCourses * (numCourses - 1)
  • prerequisites[i].length == 2
  • 0 <= ai, bi < numCourses
  • ai != bi
  • 所有[ai, bi] 互不相同

算法思路

整體思路和上一題一樣,多一個數組保存結果即可

代碼實現

class Solution {
public:vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {//1. 準備工作unordered_map<int, vector<int>> edges; // 鄰接表存圖vector<int> in(numCourses);// 保存每一個點的入度vector<int> ret;//2. 建圖for(auto& e : prerequisites){int a = e[0], b = e[1];// b->aedges[b].push_back(a);in[a]++;}//3. 拓撲排序queue<int> q;// (1). 把所有入度為0的點存進去for(int i = 0; i < numCourses; i++){if(in[i] == 0)q.push(i);}// (2). bfswhile(q.size()){int n = q.front();q.pop();ret.push_back(n);for(int a : edges[n]){in[a]--;if(in[a] == 0)q.push(a);}}// (3). 判斷是否有環for(int i : in){if(i)return vector<int>();}return ret;}
};

在這里插入圖片描述

火星詞典

題目鏈接

LCR 114. 火星詞典

題目描述

現有一種使用英語字母的外星文語言,這門語言的字母順序與英語順序不同。

給定一個字符串列表 words ,作為這門語言的詞典,words 中的字符串已經 按這門新語言的字母順序進行了排序

請你根據該詞典還原出此語言中已知的字母順序,并 按字母遞增順序 排列。若不存在合法字母順序,返回 "" 。若存在多種可能的合法字母順序,返回其中 任意一種 順序即可。

字符串 s 字典順序小于 字符串 t 有兩種情況:

  • 在第一個不同字母處,如果 s 中的字母在這門外星語言的字母順序中位于 t 中字母之前,那么 s 的字典順序小于 t
  • 如果前面 min(s.length, t.length) 字母都相同,那么 s.length < t.length 時,s 的字典順序也小于 t


示例 1:

輸入:words = [“wrt”,“wrf”,“er”,“ett”,“rftt”]
輸出:“wertf”

示例 2

輸入:words = [“z”,“x”]
輸出:“zx”

示例 3

輸入:words = [“z”,“x”,“z”]
輸出:“”
解釋:不存在合法字母順序,因此返回 “”。

提示

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 100
  • words[i] 僅由小寫英文字母組成

算法思路

  1. 先根據字典兩兩比較字母之間的先后順序,建立有向無環圖
    a. 兩層 for 循環枚舉出所有的兩個字符串的組合;
    b. 然后利用指針,根據字典序規則找出信息。
  2. 根據有向無環圖做拓撲排序找出字母的順序

代碼實現

class Solution {unordered_map<char, unordered_set<char>> edges;// 鄰接表存圖unordered_map<char,int> in;// 保存每一個點的入度bool check = false;
public:string alienOrder(vector<string>& words) {for(auto& s : words)for(auto& c : s)in[c] = 0;int n = words.size();for(int i = 0; i < n; i++){for(int j = i+1; j < n; j++){add(words[i],words[j]);if(check) return "";}}string ret;queue<char> q;for(auto& [a,b] : in){if(b == 0)q.push(a);}while(q.size()){char c = q.front();q.pop();ret += c;for(char ch : edges[c]){in[ch]--;if(in[ch] == 0)q.push(ch);}}for(auto& [a,b]:in)if(b != 0)return "";return ret;}void add(const string& s1, const string& s2){int n = min(s1.size(), s2.size());int i = 0;for(; i < n; i++){if(s1[i] != s2[i]) {char a = s1[i], b = s2[i];// a->b;if(!edges.count(a) || !edges[a].count(b)){edges[a].insert(b);in[b]++;}break;}}if(i==s2.size() && i < s1.size())check = true;}
};

在這里插入圖片描述

?? 寫在最后:以上內容是我在學習以后得一些總結和概括,如有錯誤或者需要補充的地方歡迎各位大佬評論或者私信我交流!!!

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

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

相關文章

機器學習 入門——決策樹分類

決策樹是一種直觀且強大的機器學習算法&#xff0c;適用于分類和回歸任務。本文將全面介紹決策樹分類的原理、實現、調優和實際應用。一、什么是決策樹分類1.概念決策樹分類是一種樹形結構的分類模型&#xff0c;它通過遞歸地將數據集分割成更小的子集來構建決策規則。就像我們…

虛擬機中查看和修改文件權限

在虛擬機中管理文件權限是系統管理的重要部分&#xff0c;無論是在Linux還是Windows虛擬機中。下面我將詳細介紹兩種主要系統的權限管理方法。Linux虛擬機中的文件權限管理查看文件權限使用ls命令&#xff1a;ls -l 文件名輸出示例&#xff1a;-rwxr-xr-- 1 user group 1024 Ju…

圖像處理拉普拉斯算子

AI對話記錄&#xff0c;還沒有來得及仔細驗證和推導&#xff0c;目前只是記錄 當然可以&#xff01;我們來一步步推導拉普拉斯算子在旋轉變換下保持不變的數學過程。這里以二維情況為例&#xff0c;最直觀也最常見。&#x1f9ee; 拉普拉斯算子旋轉不變性的推導&#xff08;二維…

React ahooks——副作用類hooks之useThrottleEffect

useThrottleEffect 是 ahooks 提供的節流版 useEffect&#xff0c;它在依賴項變化時執行副作用函數&#xff0c;但會限制執行頻率。一、基本語法useThrottleEffect(effect: React.EffectCallback,deps?: React.DependencyList,options?: Options )二、參數詳解2.1. effect (必…

【建模與仿真】融合畫像約束和潛在特征的深度推薦算法

導讀&#xff1a; 基于深度學習的推薦算法已成為推薦系統領域的研究趨勢。然而&#xff0c;大多數現有工作僅考慮單一的用戶與物品交互數據&#xff0c;限制了算法的預測性能。本文提出一種畫像約束的編碼方式&#xff0c;并融合隱因子模型中的潛在特征&#xff0c;豐富了推薦…

華為網路設備學習-26(BGP協議 二)路徑屬性

一、屬性分類二、屬性含義①公認必遵&#xff1a;所有BGP對等體 必須識別 且 在Update報文中攜帶1.Origin2.AS-Path3.Next hop②公認自決&#xff1a;所有BGP對等體 必須識別但可以不在Update報文中攜帶 1.Local-Preference2.ATOMIC_Aggregate③可選傳遞&#xff1a;所有BGP對…

從0搭建YOLO目標檢測系統:實戰項目+完整流程+界面開發(附源碼)

文章目錄一、前言二、專欄介紹三、已有系統介紹3.0 基于yolo通用目標檢測系統&#xff08;手把手教你修改成為自己的檢測系統&#xff09;3.1 基于yolov8柑橘檢測系統3.2 基于yolov8艦船檢測系統3.3 基于yolo11人臉檢測系統3.4 基于yolov8無人機影像光伏板缺陷檢測系統一、前言…

【測試】自動化測試工具基礎知識及基本應用

下面詳細介紹一些常用的自動化測試工具及其基本概念&#xff0c;并提供具體的示例代碼&#xff0c;幫助你更好地理解和應用這些工具。1. 自動化測試的基本概念自動化測試是通過軟件程序自動執行測試用例的過程。與手動測試相比&#xff0c;自動化測試能夠提高測試效率、減少人為…

ArcGIS的字段計算器生成隨機數

在ArcGIS的字段計算器中使用Python腳本生成0-100的隨機數&#xff0c;可以按照以下步驟操作&#xff1a; 打開屬性表&#xff0c;選擇要計算的字段打開字段計算器選擇"Python"解析器勾選"顯示代碼塊"在"預邏輯腳本代碼"中輸入以下代碼在下方表達…

【前端:Html】--1.1.基礎語法

目錄 1.HTML--簡介 2.HTML--編譯器 步驟一:啟動記事本 步驟二:用記事本來編輯 HTML 步驟三:保存 HTML 步驟四:在瀏覽器中運行 HTML 3.HTML--基礎 3.1.HTML聲明--!DOCTYPE 3.2.HTML 標題--h1 3.3.HTML 段落--p 3.3.1. 水平線--hr 3.3.2.換行符--br 3.3.3.固定格式…

FreeSWITCH 簡單圖形化界面46 - 收集打包的一些ASR服務

FreeSWITCH 簡單圖形化界面46 - 收集打包的一些ASR服務 0、一個fs的web配置界面預覽1、docker地址2、使用2.1 下載2.2 運行 3、例子3.1 下載3.2 啟動3.3 編譯mod_audio_fork或者mod_audio_stream模塊使用3.4 編寫呼叫路由和呼叫腳本呼叫路由呼叫腳本 3.5 esl捕獲識別結果3.6 其…

20250805問答課題-實現TextRank + 問題分類

textRank的工具包實現其他可能的實現方法&#xff0c;對比結果查找分類的相關算法 目錄 1. 關鍵詞提取TF-IDF TextRank 1.1. TF-IDF算法 1.2. TextRank算法 1.3. 雙算法提取關鍵詞 2. 問題分類 2.1. 預處理 2.2. 獲取BERT向量 2.3. 一級標簽預測 2.4. 二級標簽預測 3…

Memcached緩存與Redis緩存的區別、優缺點和適用場景

一、核心差異概述特性MemcachedRedis?數據結構?簡單鍵值存儲豐富數據結構&#xff08;String/Hash/List/Set等&#xff09;?持久化?不支持支持RDB和AOF兩種方式?線程模型?多線程單線程&#xff08;6.0支持多線程I/O&#xff09;?內存管理?Slab分配LRU淘汰多種淘汰策略&…

Git簡易教程

Git教程 VCS Version Control System版本控制系統 配置用戶名郵箱 配置用戶名和郵箱 git config --global user.name mihu git config --global user.email aaabbb.com初始化倉庫 從項目倉庫拉 git clone [項目地址]新建文件夾之后 git init提交操作 提交到倉庫 git add . #把…

關于Web前端安全之XSS攻擊防御增強方法

僅依賴前端驗證是無法完全防止 XSS的&#xff0c;還需要增強后端驗證&#xff0c;使用DOMPurify凈化 HTML 時&#xff0c;還需要平衡安全性與業務需求。一、僅依賴前端驗證無法完全防止 XSS 的原因及后端驗證的重要性1. 前端驗證的局限性前端驗證&#xff08;如 JavaScript 輸入…

消息系統技術文檔

消息系統技術文檔 概述 本文檔詳細說明了如何在現有的LHD通信系統中添加自己的消息類型&#xff0c;包括消息的發送、接收、解析和處理的完整流程。 系統架構 消息流程架構圖 #mermaid-svg-My7ThVxSl6aftvWK {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 微博輿情數據可視化分析-熱詞情感趨勢樹形圖

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解微博輿情數據可視化分析-熱詞情感趨勢樹形圖…

8月4日 強對流天氣藍色預警持續:多地需警惕雷暴大風與短時強降水

中央氣象臺8月4日10時繼續發布強對流天氣藍色預警,提醒廣大民眾注意防范即將到來的惡劣天氣。 預警詳情: 時間范圍: 8月4日14時至5日14時 影響區域: 雷暴大風或冰雹: 西北地區中東部、華北中北部、華南南部等地,風力可達8級以上。 短時強降水: 西北地區中東部、華北、…

C語言數據結構(4)單鏈表專題2.單鏈表的應用

1. 鏈表經典算法——OJ題目 1.1 單鏈表相關經典算法OJ題1&#xff1a;移除鏈表元素 1.2 單鏈表相關經典算法OJ題2&#xff1a;反轉鏈表 1.3 單鏈表相關經典算法OJ題3&#xff1a;合并兩個有序鏈表 1.4 單鏈表相關經典算法OJ題4&#xff1a;鏈表的中間結點 1.5 循環鏈表…

Shell 腳本發送信號給 C 應用程序,讓 C 應用程序回收線程資源后自行退出。

下面分別給出一個 Shell 腳本和 C 程序的例子&#xff0c;實現通過 Shell 腳本發送信號給 C 應用程序&#xff0c;讓 C 應用程序回收線程資源后自行退出。原理在 Linux 系統中&#xff0c;我們可以使用信號機制來實現進程間的通信。Shell 腳本可以使用 kill 命令向指定的進程發…