当前位置: 首页 > news >正文

手机网站如何推广劳动局免费培训电工

手机网站如何推广,劳动局免费培训电工,wordpress网址一大串,查关键词排名工具app文章目录问题背景:课程表问题问题建模与算法推导问题分析核心思想推导方法一:DFS 实现拓扑排序算法推导演进数据结构选择DFS 代码实现与解析正确性证明方法二:BFS 实现拓扑排序(Kahn 算法)算法推导演进数据结构选择BFS…

文章目录

  • 问题背景:课程表问题
  • 问题建模与算法推导
    • 问题分析
    • 核心思想推导
  • 方法一:DFS 实现拓扑排序
    • 算法推导演进
    • 数据结构选择
    • DFS 代码实现与解析
    • 正确性证明
  • 方法二:BFS 实现拓扑排序(Kahn 算法)
    • 算法推导演进
    • 数据结构选择
    • BFS 代码实现与解析
    • 正确性证明
  • 两种算法的比较
  • 总结

拓扑排序(Topological Sorting)是图论中一种重要的排序算法,主要用于解决有向无环图(DAG)的节点排序问题。在实际应用中,它常被用于任务调度、课程安排等存在依赖关系的场景。本文将以力扣 207 题 “课程表” 为例,详细讲解拓扑排序的两种实现方法:BFS(广度优先搜索)和 DFS(深度优先搜索)。

问题背景:课程表问题

力扣 207 题描述如下:

现在你总共有 n 门课需要学习,记为 0 到 n-1。有些课程需要先修其他课程,例如,想要学习课程 0 ,你需要先学习课程 1 ,表示为 [0,1]。给定课程总数和一个先修课程的列表,判断是否可能完成所有课程的学习?

这个问题本质上是判断一个有向图是否存在环。如果存在环,则不可能完成所有课程;如果是有向无环图,则可以通过拓扑排序确定学习顺序。

问题建模与算法推导

问题分析

题目本质上是判断一个有向图是否存在环。我们可以将问题抽象为:

  • 每门课程视为图中的一个节点(顶点)
  • 先修关系[a, b]表示从 b 到 a 的一条有向边(必须先修 b 才能修 a)
  • 问题转化为:判断该有向图是否为有向无环图(DAG)

核心思想推导

如果图中存在环,那么环上的节点之间形成了相互依赖,无法确定合理的学习顺序。例如,若存在课程 A→B→C→A 的环,则三门课程互相依赖,永远无法完成。

拓扑排序的核心思想是寻找一种线性排序,使得对于图中的任意有向边 (u, v),节点 u 都排在节点 v 之前。这种排序仅在有向无环图中存在。

我们可以通过逆向思维推导:

  1. 必须存在至少一个节点没有前驱(入度为 0),否则会形成环
  2. 移除这个节点及其所有出边,剩余图仍需满足相同性质
  3. 重复以上过程,直到所有节点都被处理(无环)或无法找到入度为 0 的节点(有环)

方法一:DFS 实现拓扑排序

算法推导演进

DFS 实现拓扑排序的思路源于对图的深度遍历特性:

  1. 状态标记:为每个节点标记三种状态
    • 0:未访问
    • 1:正在访问(处于当前递归调用栈中)
    • 2:已访问(所有子节点都已处理)
  2. 环检测逻辑
    • 当访问节点 u 时,将其标记为正在访问(1)
    • 递归访问 u 的所有邻接节点 v
    • 若 v 处于正在访问状态(1),说明从 u 到 v 存在路径,且 v 到 u 也存在路径(因为 v 在当前递归栈中),即存在环
    • 若 v 未访问(0),则继续递归访问
    • 回溯时,将 u 标记为已访问(2)
  3. 拓扑序列构建
    • 按节点被标记为已访问(2)的顺序反向排列,即可得到拓扑序列

数据结构选择

根据推导,我们需要:

  • 邻接表:存储图的结构
  • 状态数组:记录每个节点的访问状态
  • 递归栈:隐式存储当前访问路径(用于环检测)

DFS 代码实现与解析

class Solution {
private:vector<vector<int>> edges; // 邻接表vector<int> status; // 状态数组:0=未访问,1=正在访问,2=已访问bool hasCycle; // 是否存在环public:bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {edges.resize(numCourses);status.resize(numCourses, 0); // 初始化所有节点为未访问hasCycle = false;// 构建邻接表for (auto &info : prerequisites) {edges[info[1]].push_back(info[0]);}// 对每个未访问的节点进行DFSfor (int i = 0; i < numCourses && !hasCycle; ++i) {if (status[i] == 0) {dfs(i);}}return !hasCycle;}void dfs(int u) {status[u] = 1; // 标记为正在访问// 遍历所有邻接节点for (int v : edges[u]) {if (status[v] == 0) {dfs(v);if (hasCycle) return;} else if (status[v] == 1) {// 发现正在访问的节点,存在环hasCycle = true;return;}}status[u] = 2; // 标记为已访问}
};

正确性证明

假设算法错误地判定一个有环图为无环图,则环上所有节点都会被标记为已访问(2)。但在环中,当访问某个节点 u 时,必然会遇到一个处于正在访问状态(1)的节点 v(环上的前驱节点),此时算法会检测到环,与假设矛盾。因此算法正确。

方法二:BFS 实现拓扑排序(Kahn 算法)

算法推导演进

Kahn 算法是基于 BFS 的拓扑排序经典实现,其推导过程如下:

  1. 初始状态:找到所有入度为 0 的节点,这些节点没有前驱依赖,可以立即处理
  2. 处理过程
    • 选择一个入度为 0 的节点 u,将其加入拓扑序列
    • 对于 u 的每个邻接节点 v,由于 u 已处理,v 的依赖减少,因此 v 的入度减 1
    • 若 v 的入度变为 0,说明 v 的所有前驱都已处理,v 可以加入处理队列
  3. 终止条件
    • 若所有节点都被处理(拓扑序列长度等于节点总数),则无环
    • 若仍有节点未处理但已无入度为 0 的节点,则存在环

数据结构选择

根据上述推导,我们需要:

  • 邻接表:存储图的结构,快速访问每个节点的后继节点
  • 入度数组:记录每个节点当前的入度,便于判断是否可处理
  • 队列:存储待处理的入度为 0 的节点,实现 BFS

BFS 代码实现与解析

class Solution {
private:vector<vector<int>> edges; // 邻接表:edges[u]存储u的所有后继节点vector<int> indeg; // 入度数组:indeg[v]表示v的前驱节点数量public:bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {edges.resize(numCourses); // 初始化邻接表大小indeg.resize(numCourses); // 初始化入度数组大小// 构建邻接表和入度数组for (auto &info : prerequisites) {edges[info[1]].push_back(info[0]); // 前驱课程指向后继课程++indeg[info[0]]; // 增加后继课程的入度}// 将所有入度为0的节点加入队列queue<int> q;for (int i = 0; i < numCourses; ++i) {if (indeg[i] == 0) {q.push(i);}}int visited = 0; // 记录已访问的节点数while (!q.empty()) {++visited;int u = q.front();q.pop();// 遍历u的所有后继节点for (int v : edges[u]) {--indeg[v]; // 减少入度if (indeg[v] == 0) { // 若入度为0,加入队列q.push(v);}}}// 若所有节点都被访问,则无环return visited == numCourses;}
};

正确性证明

假设算法错误地判定一个有环图为无环图,则所有节点都会被加入拓扑序列。但环中的节点入度永远不会变为 0(每个节点都至少有一个前驱在环内),导致这些节点无法被处理,与假设矛盾。因此算法正确。

两种算法的比较

特性DFS 实现BFS 实现
时间复杂度O(V + E)O(V + E)
空间复杂度O(V + E)O(V + E)
适用场景检测环、求逆后序拓扑序求任意拓扑序
实现特点借助递归和状态数组借助队列和入度数组

总结

拓扑排序算法的推导过程体现了从问题抽象到具体实现的完整思维链:

  1. 将实际问题(课程安排)抽象为图论模型(有向图)
  2. 基于图的性质推导出判断有向无环图的核心逻辑
  3. 根据逻辑思路选择合适的数据结构
  4. 实现算法并证明其正确性

BFS 方法(Kahn 算法)通过维护入度和队列,直观地模拟了依赖关系的逐步解除过程;DFS 方法则利用递归栈的特性,通过状态标记巧妙地检测环的存在。两种方法各有千秋,但本质上都体现了拓扑排序的核心思想 —— 在满足所有前驱依赖的前提下,逐步构建合法的节点序列。

掌握拓扑排序不仅能解决课程表问题,更能为处理各种依赖关系问题提供通用思路,是计算机科学中不可或缺的基础算法。

http://www.dtcms.com/a/473278.html

相关文章:

  • 黄山找人做网站郑州代做网站
  • 做网站美工工资多少wordpress 去掉头部栏目
  • 技术支持公司做的网站怎么查用vue做pc端网站好吗
  • 网站备案 更名wordpress 404插件
  • 创世网站建设 优帮云百度帐号申请注册
  • 标杆网站建设自己建设网站不会咋办呀
  • 长椿街网站建设悦然wordpress建站服务
  • 番禺响应式网站建设深圳制作网站培训学校
  • 网站建设加数据库彩票计划网站怎么做
  • 哪些网站做代理网站地图 制作
  • 厦门专业网站建设建站黑龙江省住房和城乡建设部网站
  • 怎么查网站流量wordpress 桌面
  • 小说在线阅读网站怎么做网络营销推广方案案例分析
  • 邵阳做网站建设优秀营销网站设计
  • 本溪 网站建设 做网站建地方门户网站
  • 制作一个网站需要多少小时如何建设本地网站
  • 做网站讯息制作h5的基本流程
  • 河南城乡建设部网站一定要用c 做网站吗
  • wordpress 插件 留言seo推广分析
  • 做家装壁纸的网站宇泽佛山网站建设
  • 创建一个网站的项目体现项目完成速度因素的网架厂家
  • 泰安网站开发公司cms视频系统大全
  • 太原网站优化怎么做网络seo优化公司
  • 网站全屏宽度是多少合适seo基础课程
  • 用手机建网站世界优秀摄影作品网站
  • php模板建站百度做网站续费费用
  • 专业做网站排名的人北京搜索引擎推广服务
  • wordpress适合下载站的主题国外购物网站app
  • php网站开发实例教程简介寮步做网站公司
  • 营销网站建设推广安徽省工程建设信息网职称查询