【排序算法】排序算法-拓扑排序

article/2025/10/1 6:40:22

拓扑排序

  • 相关概念
    • AOV网
    • 拓扑排序
  • 实现思路
    • 实现过程
  • 代码
    • 测试
      • 测试类
      • 测试样例

相关概念

AOV网

  • 一项大的工程常被分为多个小的子工程
    • 子工程之间可能存在一定的先后顺序,即某些子工程必须在其他的一些子工程完成后才能开始
  • 在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,子工程被称为活动(Activity)
    • 以顶点表示活动、有向边表示活动之间的先后关系,这样的图简称为AOV网
  • 标准的AOV网必须是一个有向无环图(Directed Acyclic Graph, 简称 DAG)

在这里插入图片描述

拓扑排序

在这里插入图片描述

  • 前驱活动: 有向边起点的活动称为终点的前驱活动
    • 只有当一个活动的前驱全部都完成后,这个活动才能进行
  • 后继活动: 有向边终点的活动称为起点的后继活动

在这里插入图片描述

  • 什么是拓扑排序?
    • 将AOV网中所有活动排成一个序列,使得每个活动的前驱活动都排在该活动的前面
    • 比如上图的拓扑排序结果是: A、B、C、D、E、F或者是A、B、D、C、E、F(结果并不一定是唯一的)

实现思路

  • 可以使用卡恩算法(kahn于1962年提出)完成拓扑排序
  • 假设L是存放拓扑排序结果的列表
  1. 把所有入度为0的顶点放入L中,然后把这些顶点从图中去掉
  2. 重复操作1,直到找不到入度为0的顶点
  • 如果此时L中的元素个数和顶点总数相同,说明拓扑排序完成
  • 如果此时L中的元素个数少于顶点总数,说明原图中存在环,无法进行拓扑排序

在这里插入图片描述
在这里插入图片描述

实现过程

在这里插入图片描述

  • 首先维护一张入度表和一个list集合,和一个队列
    入度表
    在这里插入图片描述
    队列(初始化将所有的入度为0的顶点放到队列中)
    在这里插入图片描述

  • 将C出队,放到list中,并且遍历所有以C为为起点的边的终点的集合,将这些点的入度-1,如果这些点中存在-1之后入度为0的点,将其加入到队列中。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 将队列中的顶点E出队,放到list中,并且遍历所有以E为为起点的边的终点的集合,将这些点的入度-1, 此轮-1后,点A的入度为0,入队
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • A点出队,放到list集合中,以A点为起点的边的终点集合的入度-1,此轮之后,B,D两点的入度为0,加入到队列中
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • D点出队,加入list集合中
    在这里插入图片描述
    在这里插入图片描述

  • B点出队,加入到list集合中,更新入度表,点F入队在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 点F出队,加入到list集合中
    在这里插入图片描述
    在这里插入图片描述

代码

  • 图的接口类
public interface Graph<V, E> {/*** 打印边*/void print();/*** 边的数量* @return*/int edgeSize();/*** 顶点的数量* @return*/int vertexSize();/*** 添加顶点* @param v*/void addVertex(V v);/*** 添加边(无权值)* @param from* @param to*/void addEdge(V from, V to);/*** 添加边(有权值)* @param from* @param to* @param weight*/void addEdge(V from, V to, E weight);/*** 移除顶点* @param v*/void removeVertex(V v);/*** 移除边* @param from* @param to*/void removeEdge(V from, V to);/*** 拓扑排序* @return*/List<V> topologicalSort();}
  • 图的实现类
public class ListGraph<V, E> implements Graph<V, E> {private Map<V, Vertex<V, E>> vertices = new HashMap<>();private Set<Edge<V, E>> edges = new HashSet<>();/*** 打印*/@Overridepublic void print() {//打印顶点vertices.forEach((v, vertex) -> {System.out.println(v);System.out.println("out-----------");System.out.println(vertex.outEdges);System.out.println("in------------");System.out.println(vertex.inEdges);});//打印边edges.forEach(edge -> System.out.println(edge));}@Overridepublic int edgeSize() {return edges.size();}@Overridepublic int vertexSize() {return vertices.size();}@Overridepublic void addVertex(V v) {if (vertices.containsKey(v)) {return;}//添加顶点vertices.put(v, new Vertex<>(v));}@Overridepublic void addEdge(V from, V to) {addEdge(from, to, null);}@Overridepublic void addEdge(V from, V to, E weight) {Vertex<V, E> fromVertex = vertices.get(from);//如果map中不存在value=from的顶点,则创建并放到map中if (fromVertex == null) {fromVertex = new Vertex<>(from);vertices.put(from, fromVertex);}//如果map中不存在value=to的顶点,则创建并放到map中Vertex<V, E> toVertex = vertices.get(to);if (toVertex == null) {toVertex = new Vertex<>(to);vertices.put(to, toVertex);}//根据两个顶点创建边Edge<V, E> edge = new Edge<>(fromVertex, toVertex);//赋权值edge.weight = weight;//如果edge这条边已存在,就删除edge这条边,在from顶点的outEdges集合中删掉这条边,在to顶点的inEdges集合中删除这条边,直接删除原来的边,重新添加新的边,因为权值可能更新了。if (fromVertex.outEdges.remove(edge)) {toVertex.inEdges.remove(edge);}//再次把边添加到两个顶点的两个集合中。fromVertex.outEdges.add(edge);toVertex.inEdges.add(edge);//将边添加到集合edges中edges.add(edge);}@Overridepublic void removeVertex(V v) {Vertex<V, E> vertex = vertices.remove(v);//如果map中不存在value=v的顶点,直接返回if (vertex == null) {return;}//删除边集和Edges中的所有和顶点vertex相关的边for (Iterator<Edge<V, E>> iterator = vertex.outEdges.iterator(); iterator.hasNext();) {Edge<V, E> edge = iterator.next();edge.to.inEdges.remove(edge);iterator.remove();edges.remove(edge);}for (Iterator<Edge<V, E>> iterator = vertex.inEdges.iterator(); iterator.hasNext();) {Edge<V, E> edge = iterator.next();edge.from.outEdges.remove(edge);iterator.remove();edges.remove(edge);}}@Overridepublic void removeEdge(V from, V to) {Vertex<V, E> fromVertex = vertices.get(from);//如果map中不存在value=from的顶点,则肯定也没有以from为顶点的边if (fromVertex == null) {return;}//如果map中不存在value=to的顶点,则肯定也没有以to为顶点的边Vertex<V, E> toVertex = vertices.get(to);if (toVertex == null) {return;}//根据两个顶点创建边Edge<V, E> edge = new Edge<>(fromVertex, toVertex);//删除对应的边if (fromVertex.outEdges.remove(edge)) {toVertex.inEdges.remove(edge);edges.remove(edge);}}/*** 顶点*/static class Vertex<V, E> {V value;Set<Edge<V, E>> inEdges = new HashSet<>();Set<Edge<V, E>> outEdges = new HashSet<>();public Vertex(V value) {this.value = value;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Vertex<V, E> vertex = (Vertex<V, E>) o;return Objects.equals(value, vertex.value);}@Overridepublic int hashCode() {return Objects.hash(value);}@Overridepublic String toString() {return value == null ? "null" : value.toString();}}@Overridepublic List<V> topologicalSort() {//拓扑排序后的结果List<V> list =new ArrayList<>();Queue<Vertex<V,E>> queue = new LinkedList<>();//使用map维护每个顶点的出度Map<Vertex<V, E>, Integer> ins = new HashMap<>();vertices.forEach((v, vertex) -> {//获取以vertex为终点的边的个数,即入度大小int in = vertex.inEdges.size();//如果入度为0,放到队列中,否则将顶点的入度放到map(顶点 -> 顶点的入度)中if (in == 0) {queue.offer(vertex);} else {ins.put(vertex, in);}});//当队列非空while (!queue.isEmpty()) {//队列元素出队Vertex<V, E> vertex = queue.poll();//放入返回结果中list.add(vertex.value);//遍历所有以该顶点为起点的边的集合,将边的重点的度for (Edge<V, E> edge : vertex.outEdges) {//终点的入度-1int toIn = ins.get(edge.to) - 1;//入度为0加入队列,否则更新map中维护的顶点的入度if (toIn == 0) {queue.offer(edge.to);} else {ins.put(edge.to, toIn);}}}return list;}/*** 边*/static class Edge<V, E> {Vertex<V, E> from;Vertex<V, E> to;E weight;public Edge(Vertex<V, E> from, Vertex<V, E> to) {this.from = from;this.to = to;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Edge<V, E> edge = (Edge<V, E>) o;return Objects.equals(from, edge.from) && Objects.equals(to, edge.to);}@Overridepublic int hashCode() {return Objects.hash(from, to);}@Overridepublic String toString() {return "Edge{" +"from=" + from +", to=" + to +", weight=" + weight +'}';}}}

测试

测试类

public class TopologicalSort {public static final Object[][] TOPO = {{0, 2},{1, 0},{2, 5}, {2, 6},{3, 1}, {3, 5}, {3, 7},{5, 7},{6, 4},{7, 6}};/*** 生成有向图*/private static Graph<Object, Double> directedGraph(Object[][] data) {Graph<Object, Double> graph = new ListGraph<>();for (Object[] edge : data) {if (edge.length == 1) {graph.addVertex(edge[0]);} else if (edge.length == 2) {graph.addEdge(edge[0], edge[1]);}}return graph;}public static void main(String[] args) {//构建有向图Graph<Object, Double> graph = directedGraph(TOPO);//对图进行拓扑排序List<Object> list = graph.topologicalSort();System.out.println(list);}}

测试样例

  • 构造的图如下图所示
    在这里插入图片描述
  • 排序的输出结果
    在这里插入图片描述

http://chatgpt.dhexx.cn/article/FhgFiFyr.shtml

相关文章

算法提升:图的拓扑排序算法

目录 概念 思路 代码 概念 拓扑序列&#xff1a;一些活动&#xff0c;其中某些活动必须在另一些活动完成之后才能开始&#xff0c;一定是无环的有向图&#xff0c;称为AOV网。 拓扑排序&#xff0c;其实就是对一个有向图构造拓扑序列的过程。构造时会有两个结果&#xff1a…

leetcode-拓扑排序算法

拓扑排序原理 拓扑排序算法分析&#xff08;通俗易懂&#xff09;_hongjie_lin-CSDN博客_拓扑排序算法 207 课程表 bfs和dfs都可以。先来看一下bfs。 思路是&#xff1a;入度法&#xff0c;入度为0的时候&#xff0c;表示这门课程没有先修课程了&#xff0c;可以学习这门课程了…

2022.3.24 图论——拓扑排序算法

文章目录 一、拓扑排序简介二、例题1.题目2.分析3.代码 一、拓扑排序简介 1.Topological Sorting&#xff0c;指的是一个DAG(Directed Acyclic Graph)即有向图所有顶点满足一定条件的线性序列。 拓扑序列应满足两个条件&#xff1a; 每个点都只出现一次 如果存在一条从A指向B…

数据结构——图——拓扑排序算法

数据结构——图——拓扑排序算法 对AOV网进行拓扑排序的基本思路是:从AOV网中选择一个入度为0的顶点输出&#xff0c;然后删去此顶点&#xff0c;并删除以此顶点为尾的弧&#xff0c;继续重复此步骤&#xff0c;直到输出全部顶点或者AOV 网中不存在入度为0的顶点为止。 首先我…

拓扑排序详解(包含算法原理图解、算法实现过程详解、算法例题变式全面讲解等)

前置知识 有向无环图 在图论中&#xff0c;如果一个有向图无法从某个顶点出发经过若干条边回到该点&#xff0c;则这个图是一个有向无环图&#xff08;DAG图&#xff09;。 如图所示。 入度 对于一个有向图&#xff0c;若x点指向y点&#xff0c;则称x点为y点的入度。 出度…

拓扑排序算法分析(通俗易懂)

拓扑排序&#xff08;其实是一种依赖关系&#xff09;&#xff1a;对于有向且无环的图来说&#xff0c;当前这个节点的依赖来其之前已经完成了。 下面附上一个图让大伙更好的理解&#xff1a; 比如这个图&#xff1a;B需要依赖A才能完成&#xff0c;A需要依赖C和D才能完成&…

拓扑排序算法详讲

经过一天的专研,终于明白了拓扑排序算法,写篇博客记录一下心得. 一.拓扑排序介绍 在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为AOV网. 设G(V,E)是一个具有n个顶点的有向图,v中的顶点序列v1,v2…,vn,满足若从…

C++ 拓扑排序算法

拓扑排序 有向无环图 如果一个有向图的任意顶点都无法通过一些有向边回到自身&#xff0c;那么称这个有向图为有向无环图。 拓扑排序 拓扑排序是将有向无环图G的所有顶点排成一个线性序列&#xff0c;使得对图G中的任意两个顶点u、v&#xff0c;如果存在边u->v&#xff0c;那…

拓扑排序

拓扑排序 一、拓扑排序的定义&#xff1a; 先引用一段百度百科上对于拓扑排序的定义&#xff1a; 对一个有向无环图 ( Directed Acyclic Graph 简称 DAG ) G 进行拓扑排序&#xff0c;是将 G 中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点 u 和 v &#xff0c…

拓扑排序算法

拓扑排序介绍 拓扑排序(Topological Order)是指&#xff0c;将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。 这样说&#xff0c;可能理解起来比较抽象。下面通过简单的例子进行说明&#xff01; 例如&#xff0c;一个项目包括A、B、C…

经典算法之拓扑排序

定义&#xff1a; 把AOV网&#xff08;用定点表示活动&#xff0c;用弧表示活动间优先关系的有向图&#xff09;络中各个顶点按照它们互相之间的优先关系排列成一个线性序列的过程叫做拓扑排序。 方法&#xff1a; 在有向图中选一个没有前驱的顶点并且输出从图中删除该顶点和…

拓扑排序(topological sorting)介绍及Python实现

目录 1. 拓扑排序 2. 拓扑排序存在的前提 3. 拓扑排序的唯一性问题 4. 拓扑排序算法原理 4.1 广度优先遍历 4.2 深度优先遍历 5. 代码实现 5.1 Graph类的实现 5.2 广度优先搜索 5.3 深度优先搜索简易版&#xff08;无loop检测&#xff09; 5.4 深度优先搜索完整版 …

【算法】拓扑排序

今天学习拓扑排序。如果一个有向图的任意顶点都无法通过一些有向边回到自身&#xff0c;那么称这个有向图为有向无环图&#xff08;Directed Acyclic Graph&#xff0c;DAG&#xff09;。拓扑排序就是将有向无环图的所有顶点排序&#xff0c;使得图中任意两个点 u、v&#xff0…

拓扑排序及算法实现

一、拓扑排序概念 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序&#xff0c;是将G中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点u和v&#xff0c;若边<u,v>∈E(G)&#xff0c;则u在线性序列中出现在v之前。通常&#xff0c;这样的线性…

利用迭代法求平方根——迭代法开平方运算

一、题目 用迭代法求&#xff1a; 的值。要求精度为0.00001&#xff0c;即 二、迭代公式 求平方根的迭代公式为&#xff1a; 当满足 时&#xff0c;这时的即是求得的根。如果 得到是精确值。如果不为0&#xff0c;则是近似值。 三、C代码 先给出开平方运算的函数。再给出主…

【算法】牛顿迭代法求平方根的原理和误差分析

前言 在《算法(第四版)》中的P23页&#xff0c;给出了经典的利用牛顿迭代法求平方根的算法&#xff0c;牛顿迭代法在数值计算中应用十分广泛&#xff0c;但是在看书中的代码时&#xff0c;我最困惑的是其中对收敛条件的判断&#xff0c;经过查阅资料和论坛&#xff0c;找到…

使用迭代法来求a的平方根

今天朋友问我一道使用迭代法求a的平方根的题&#xff0c;感觉受益匪浅&#xff0c;与诸君相分享 首先我们来看一下题目 我们也无需了解迭代法是什么原理&#xff0c;根据这个题目可以分析得到&#xff0c;需要使用while循环&#xff0c;下面是我的代码实践 #define _CRT_SEC…

利用牛顿迭代法求平方根

求n的平方根&#xff0c;先假设一猜测值X0 1&#xff0c;然后根据以下公式求出X1&#xff0c;再将X1代入公式右边&#xff0c;继续求出X2…通过有效次迭代后即可求出n的平方根&#xff0c;Xk1 先让我们来验证下这个巧妙的方法准确性&#xff0c;来算下2的平方根 (Computed b…

牛顿迭代法求解平方根

一个实例迭代简介牛顿迭代法 牛顿迭代法简介简单推导泰勒公式推导延伸与应用 一个实例 //java实现的sqrt类和方法 public class sqrt {public static double sqrt(double n){if (n<0) return Double.NaN;double err 1e-15;double t n;while (Math.abs(t - n/t) > err…

牛顿迭代法求一个数的平方根(python)

# !/usr/bin/env python # -*- coding: utf-8 -*- """ Author: P♂boy License: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited. Contact: 17647361832163.com Software: Pycharm File: sqrt.py.py Time: 2018/11/19 16:22 Desc:牛顿…