12. 图的进阶

news2024/11/17 7:18:59

12. 图的进阶

12.1 有向图

在实际生活中,很多应用相关的图都是有方向性的,最直观的就是网络,可以从A页面通过链接跳转到B页面,那么a和b连接的方向是a->b,但不能说是b->a,此时我们就需要使用有向图来解决这一类问题,它和我们之前学习的无向图,最大的区别就在于连接是具有方向的,在代码的处理上也会有很大的不同。

image-20230326103314638

12.1.1 有向图的定义及相关术语

定义:

有向图是一副具有方向性的图,是由一组顶点和一组有方向的边组成的,每条方向的边都连着一对有序的顶点。

出度:

由某个顶点指出的边的个数称为该顶点的出度。

入度:

指向某个顶点的边的个数称为该顶点的入度。

有向路径:

由一系列顶点组成,对于其中的每个顶点都存在一条有向边,从它指向序列中的下一个顶点。

有向环:

一条至少含有一条边,且起点和终点相同的有向路径。

image-20230326104550401

一副有向图中两个顶点v和w可能存在以下四种关系:

  1. 没有边相连;

  2. 存在从v到w的边v—>w;

  3. 存在从w到v的边w—>v;

  4. 既存在w到v的边,也存在v到w的边,即双向连接

理解有向图是一件比较简单的,但如果要通过眼睛看出复杂有向图中的路径就不是那么容易了。

image-20230326104718361

12.1.2 有向图API设计

类名Digraph
构造方法Digraph(int V):创建一个包含V个顶点但不包含边的有向图
成员方法1.public int V():获取图中顶点的数量
2.public int E():获取图中边的数量
3.public void addEdge(int v,int w):向有向图中添加一条边 v->w
4.public Queue adj(int v):获取由v指出的边所连接的所有顶点
5.private Digraph reverse():该图的反向图
成员变量1.private final int V: 记录顶点数量
2.private int E: 记录边数量
3.private Queue[] adj: 邻接表

在api中设计了一个反向图,其因为有向图的实现中,用adj方法获取出来的是由当前顶点v指向的其他顶点,如果能得到其反向图,就可以很容易得到指向v的其他顶点。

12.1.3 有向图实现

package com.ynu.Java版算法.U12_图的进阶.T1_有向图;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @author ybh
 * @date 2023.03.26 10:56
 */
public class Digraph {

    // 记录顶点的数量
    private int V;

    // 边的数量
    private int E;

    // 邻接表
    private Queue<Integer>[] adj;


    public Digraph(int V) {
        this.V = V;
        E = 0;
        // 邻接表的大小应该和顶点数量相同
        this.adj = new Queue[V];

        // 初始化邻接表的空队列
        for (int i = 0; i < adj.length; i++) {
            adj[i] = new LinkedList<>();
        }

    }


    // 获取顶点数量
    public int getV() {
        return V;
    }

    //获取边的数目
    public int E() {
        return E;
    }

    //向有向图中添加一条边 v->w
    public void addEdge(int v, int w) {
        //由于有向图中边是有向的,v->w 边,只需要让w出现在v的邻接表中,而不需要让v出现在w的邻接表中
        //无向图的话两个都要加
        adj[v].add(w);
        //边的数目自增1
        E++;
    }


    //获取由v指出的边所连接的所有顶点
    public Queue<Integer> adj(int v) {
        return adj[v];
    }

    //该图的反向图
    private Digraph reverse() {
        //创建新的有向图对象
        Digraph r = new Digraph(V);
        //遍历0~V-1所有顶点,拿到每一个顶点v
        for (int v = 0; v < V; v++) {
        //得到原图中的v顶点对应的邻接表,原图中的边为 v->w,则反向图中边为w->v;
            for (Integer w : adj(v)) {
                r.addEdge(w, v);
            }
        }
        return r;
    }

}

12.2 拓扑排序

​ 在现实生活中,我们经常会同一时间接到很多任务去完成,但是这些任务的完成是有先后次序的。以我们学习java学科为例,我们需要学习很多知识,但是这些知识在学习的过程中是需要按照先后次序来完成的。从java基础,到jsp/servlet,到ssm,到springboot等是个循序渐进且有依赖的过程。在学习jsp前要首先掌握java基础和html基础,学习ssm框架前要掌握jsp/servlet之类才行。

image-20230326171609360

为了简化问题,我们使用整数为顶点编号的标准模型来表示这个案例:

image-20230326114225024

此时如果某个同学要学习这些课程,就需要指定出一个学习的方案,我们只需要对图中的顶点进行排序,让它转换为一个线性序列,就可以解决问题,这时就需要用到一种叫拓扑排序的算法。

拓扑排序:

给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素,此时就可以明确的表示出每个顶点的优先级。下列是一副拓扑排序后的示意图:

image-20230326114256979

能进行拓扑排序的必然是一个无环的图。

12.2.1 检测有向图中的环

如果学习x课程前必须先学习y课程,学习y课程前必须先学习z课程,学习z课程前必须先学习x课程,那么一定是有问题了,我们就没有办法学习了,因为这三个条件没有办法同时满足。其实这三门课程x、y、z的条件组成了一个环:

image-20230326114346872

因此,如果我们要使用拓扑排序解决优先级问题,首先得保证图中没有环的存在。

12.2.1.2 检测有向环的API设计
类名DirectedCycle
构造方法DirectedCycle(Digraph G):创建一个检测环对象,检测图G中是否有环
成员方法1.private void dfs(Digraph G,int v):基于深度优先搜索,检测图G中是否有环
2.public boolean hasCycle():判断图中是否有环
成员变量1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private boolean hasCycle: 记录图中是否有环
3.private boolean[] onStack:索引代表顶点,使用栈的思想,记录当前顶点有没有已经处于正在搜索的有向路径上
12.2.1.2 检测有向环实现

在API中添加了onStack[] 布尔数组,索引为图的顶点,当我们深度搜索时:

  1. 如果当前顶点正在搜索,则把对应的onStack数组中的值改为true,标识进栈
  2. 如果当前顶点搜索完毕,则把对应的onStack数组中的值改为false,标识出栈;
  3. 如果即将要搜索某个顶点,但该顶点已经在栈中,则图中有环;

image-20230326123505014

代码:

package com.ynu.Java版算法.U12_图的进阶.T1_有向图;

import java.util.LinkedList;
import java.util.Queue;

/**
 * @author ybh
 * @date 2023.03.26 10:56
 */
public class Digraph {

    // 记录顶点的数量
    private int V;

    // 边的数量
    private int E;

    // 邻接表
    private Queue<Integer>[] adj;


    public Digraph(int V) {
        this.V = V;
        E = 0;
        // 邻接表的大小应该和顶点数量相同
        this.adj = new Queue[V];

        // 初始化邻接表的空队列
        for (int i = 0; i < adj.length; i++) {
            adj[i] = new LinkedList<>();
        }

    }


    // 获取顶点数量
    public int V() {
        return V;
    }

    //获取边的数目
    public int E() {
        return E;
    }

    //向有向图中添加一条边 v->w
    public void addEdge(int v, int w) {
        //由于有向图中边是有向的,v->w 边,只需要让w出现在v的邻接表中,而不需要让v出现在w的邻接表中
        //无向图的话两个都要加
        adj[v].add(w);
        //边的数目自增1
        E++;
    }


    //获取由v指出的边所连接的所有顶点
    public Queue<Integer> adj(int v) {
        return adj[v];
    }

    //该图的反向图
    private Digraph reverse() {
        //创建新的有向图对象
        Digraph r = new Digraph(V);
        //遍历0~V-1所有顶点,拿到每一个顶点v
        for (int v = 0; v < V; v++) {
        //得到原图中的v顶点对应的邻接表,原图中的边为 v->w,则反向图中边为w->v;
            for (Integer w : adj(v)) {
                r.addEdge(w, v);
            }
        }
        return r;
    }

}




package com.ynu.Java版算法.U12_图的进阶.T2_拓扑排序;

import com.ynu.Java版算法.U12_图的进阶.T1_有向图.Digraph;

/**
 * @author ybh
 * @date 2023.03.26 13:41
 */
public class DirectedCycleTest {
    public static void main(String[] args) {

        // 创建有4个顶点的图
        Digraph digraph = new Digraph(4);
        digraph.addEdge(0,1);
        digraph.addEdge(1,2);
        digraph.addEdge(2,3);
        digraph.addEdge(3,0);

        DirectedCycle cycle = new DirectedCycle(digraph);

        System.out.println("图"+ (cycle.hasCycle()?"有环":"无环"));

    }
}

12.2.2 有向无环图基于深度优先的顶点排序

​ 如果要把图中的顶点生成线性序列其实是一件非常简单的事,之前我们学习并使用了多次深度优先搜索,我们会发现其实深度优先搜索有一个特点,那就是在一个连通子图上,每个顶点只会被搜索一次,如果我们能在深度优先搜索的基础上,添加一行代码,只需要将搜索的顶点放入到线性序列的数据结构中,我们就能完成这件事。

12.2.2.1 顶点排序API设计
类名DepthFirstOrder
构造方法DepthFirstOrder(Digraph G):创建一个顶点排序对象,生成顶点线性序列;
成员方法1.private void dfs(Digraph G,int v):基于深度优先搜索,生成顶点线性序列
2.public Stack reversePost():获取顶点线性序列
成员变量1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索
2.private Stack reversePost: 使用栈,存储顶点序列
12.2.2.2 顶点排序实现

在API的设计中,我们添加了一个栈reversePost用来存储顶点,当我们深度搜索图时,每搜索完毕一个顶点,把该顶点放入到reversePost中,这样就可以实现顶点排序。

image-20230326172323934

package com.ynu.Java版算法.U12_图的进阶.T2_拓扑排序.S2_深度优先顶点排序;

import com.ynu.Java版算法.U12_图的进阶.T1_有向图.Digraph;

import java.util.Queue;
import java.util.Stack;

/**
 * @author ybh
 * @date 2023.03.26 17:25
 */
public class DepthFirstOrder {

    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;

    //使用栈,存储顶点序列
    private Stack<Integer> reversePost;

    //创建一个检测环对象,检测图G中是否有环
    public DepthFirstOrder(Digraph digraph) {
        //创建一个和图的顶点数一样大小的marked数组
        this.marked = new boolean[digraph.V()];
        this.reversePost = new Stack<>();

        // 遍历搜索图的每个节点
        for (int i = 0; i < digraph.V(); i++) {
            if (!marked[i]){
                //如果当前顶点没有搜索过,则搜索
                dfs(digraph,i);
            }
        }

    }

    //基于深度优先搜索。
    private void dfs(Digraph digraph, int v) {

        //把当前顶点标记为已搜索
        marked[v] = true;

        //遍历v顶点的邻接表,得到每一个顶点w
        Queue<Integer> adj = digraph.adj(v);
        for (Integer w : adj) {
            //如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
            if (!marked[w]){
                dfs(digraph,w);
            }
        }


        //当前顶点已经搜索完毕,让当前顶点入栈
        reversePost.push(v);
    }

    //获取顶点线性序列
    public Stack<Integer> reversePost(){
        return reversePost;
    }
}

12.2.3 拓扑排序实现

前面已经实现了环的检测以及顶点排序,那么拓扑排序就很简单了,基于一幅图,先检测有没有环,如果没有环,则调用顶点排序即可。

API设计:

类名TopoLogical
构造方法TopoLogical(Digraph G):构造拓扑排序对象
成员方法1.public boolean isCycle():判断图G是否有环
2.public Stack order():获取拓扑排序的所有顶点
成员变量1.private Stack order: 顶点的拓扑排序

代码:

package com.ynu.Java版算法.U12_图的进阶.T2_拓扑排序.S2_深度优先顶点排序;

import com.ynu.Java版算法.U12_图的进阶.T1_有向图.Digraph;

import java.util.Queue;
import java.util.Stack;

/**
 * @author ybh
 * @date 2023.03.26 17:25
 */
public class DepthFirstOrder {

    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;

    //使用栈,存储顶点序列
    private Stack<Integer> reversePost;

    //创建一个检测环对象,检测图G中是否有环
    public DepthFirstOrder(Digraph digraph) {
        //创建一个和图的顶点数一样大小的marked数组
        this.marked = new boolean[digraph.V()];
        this.reversePost = new Stack<>();

        // 遍历搜索图的每个节点
        for (int i = 0; i < digraph.V(); i++) {
            if (!marked[i]){
                //如果当前顶点没有搜索过,则搜索
                dfs(digraph,i);
            }
        }

    }

    //基于深度优先搜索。
    private void dfs(Digraph digraph, int v) {

        //把当前顶点标记为已搜索
        marked[v] = true;

        //遍历v顶点的邻接表,得到每一个顶点w
        Queue<Integer> adj = digraph.adj(v);
        for (Integer w : adj) {
            //如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
            if (!marked[w]){
                dfs(digraph,w);
            }
        }


        //当前顶点已经搜索完毕,让当前顶点入栈
        reversePost.push(v);
    }

    //获取顶点线性序列
    public Stack<Integer> reversePost(){
        return reversePost;
    }
}

12.3 加权无向图

持续学习中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452103.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8 年后端开发,API 设计的学习方法分享

笔者目前在参与一个开源项目&#xff0c;平时接触多的也是 API 相关的核心功能开发&#xff0c;经常会有读者私信我&#xff0c;对于开发新人而言&#xff0c;如何快速学习 API 设计&#xff0c;我简单总结了一下&#xff1a; 1. 学习基础知识&#xff1a;学习HTTP、RESTful AP…

Vue表单基本操作-收集表单数据

收集表单数据 使用vue中的v-model收集表单里面的数据&#xff0c;不同的表单元素配合v-model会有不同的写法和技巧 本次的表单元素包括&#xff1a;文本框&#xff0c;单选&#xff0c;多选&#xff0c;下拉框&#xff0c;文本域 编写表单元素 首先编写表单元素&#xff0c;…

IT项目管理 PPT 选择题【太原理工大学】

单选题&#xff0c;10*2分20分 填空题&#xff0c;10*2分20分 判断题&#xff0c;10*1分10分 画图题&#xff0c;10分 进度计算题&#xff0c;15分 成本计算题&#xff0c;15分 简答题&#xff0c;5*2分10分 重点章节&#xff1a;3、4、5、6、7章 我觉得小题应该是在 PP…

如何在短时间内完成短视频app的上线及推广?

作为一款充满活力和创意的短视频app&#xff0c;想要快速上线并赢得用户的喜爱是一项艰巨的任务。在本文中&#xff0c;我们将探讨如何在短时间内完成短视频app的上线及推广。 短视频app搭建 首先&#xff0c;要成功地上线一款短视频app&#xff0c;您需要一套高质量的小视频…

从零入门激光SLAM(八)——ROS常用消息

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

Scrapy框架 -- POST请求实现案例

一、创建一个Scrapy项目fanyi&#xff0c;并进入该项目创建&#xff0c;fanyipc爬虫文件 scrapy startproject fanyi cd fanyi scrapy genspider example example.com 二、修改配置文件settings&#xff0c;关闭君子协议&#xff0c;只看报错信息&#xff0c;自定义UA头 ROB…

Vue.prototype 详解及使用

前言&#xff1a; 我们可能会在很多组件里用到数据/实用工具&#xff0c;但是不想污染全局作用域。这种情况下&#xff0c;可以通过在原型上定义它们使其在每个 Vue 的实例中可用。 1. 基本示例 在main.js中添加一个变量到 Vue.prototype Vue.prototype.$appName My App这…

ChatGPT应用篇:如何快速生成精美PPT提高工作效率-附资料下载

一、ChatGPT生成markdown源代码 问&#xff1a; 我想做一份ChatGPT变现方法的PPT&#xff0c;请生成丰富的教学展示内容&#xff0c;因为生成PPT是需要MarkDown格式的&#xff0c;请您输出Markdown格式的内容 ChatGPT回复&#xff1a; 二、Mindshow登录/注册 用浏览器打开Mi…

公司为什么禁止SpringBoot项目使用Tomcat?

前言 在SpringBoot框架中&#xff0c;我们使用最多的是Tomcat&#xff0c;这是SpringBoot默认的容器技术&#xff0c;而且是内嵌式的Tomcat。 同时&#xff0c;SpringBoot也支持Undertow容器&#xff0c;我们可以很方便的用Undertow替换Tomcat&#xff0c;而Undertow的性能和…

Mac 安装Charles抓包工具及使用教程(什么,都什么时候了还不会抓包)

Mac 安装Charles抓包工具及使用教程 一、抓包工具对比二、安装Charles三、网页抓包 一、抓包工具对比 这五个工具都是比较常用的抓包工具&#xff0c;具体哪个更适合你需要根据你的具体需求和使用习惯来决定。以下是它们各自的优缺点&#xff1a; Charles&#xff1a;功能强大…

[LeetCode周赛复盘] 第 342 场周赛20230423

[LeetCode周赛复盘] 第 342 场周赛20230423 一、本周周赛总结二、 6387. 计算列车到站时间1. 题目描述2. 思路分析3. 代码实现 三、6391. 倍数求和1. 题目描述2. 思路分析3. 代码实现 四、6390. 滑动子数组的美丽值1. 题目描述2. 思路分析3. 代码实现 五、 6392. 使数组所有元…

Grid (基础DP)

题目&#xff1a; 给一个 HW 的网格&#xff0c;网格由‘.’和‘#’组成&#xff0c;一开始在左上角 (1,1)(1,1) 每一步只能向右或向下走&#xff0c;不能经过 # 格子&#xff0c;求走到右下角 (H,W) 有多少种走法。 其中 2<H,W<1000&#xff1b; 答案对 10^97 取模。…

ZBlog安装SSL证书

Z-BlogPHP简介 Z-BlogPHP是一款强大的博客式建站系统和CMS程序Z-BlogPHP支持PHP 5.2 - 7.4、8.0 - 8.1,可运行在市面上所有的操作系统和WEB服务器之上。 大部分部署ZBlogPHP程序使用Linux比较普遍&#xff0c;这是介绍使用宝塔控制面板安装SSL证书方法&#xff1a; 1、进入Gw…

JDBC操作数据库

数据库介绍 数据库是一种存储结构&#xff0c;允许使用各种格式输入、处理和检索数据&#xff0c;不必再每次需要数据时重新输入。当前比较流行的数据库主要有MySQL、Oracle、SQL Server等 使用JDBC操作数据库&#xff0c;SQL语句是比不可少的&#xff0c;SQL是一种结构化查询…

安装配置 ZLMediaKit

一、ZLMediaKit 库简介 ZLMediaKit 是一个基于 C11 的高性能运营级流媒体服务框架 官方写的项目特点&#xff1a; 基于 C11 开发&#xff0c;避免使用裸指针&#xff0c;代码稳定可靠&#xff0c;性能优越。 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV/GB28181/MP4…

【数据分析之道-Pandas(一)】Series操作

文章目录 专栏导读1、Series简介2、创建Series3、Series索引4、Series切片 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN Python领域新星创作者&#xff0c;专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》&#xff0c;本专栏针对大学生、初级数据分析工…

光线追踪 1(基本概念Whitted-Style Ray Tracing)

为什么要光线追踪 光栅化存在什么问题 无法实现软阴影&#xff08;光源不是严格点光源时&#xff09; 无法解决光线反射超过一次的情况 相对来说光照效果的质量低 光线追踪的特点 准确慢&#xff08;相对光栅化&#xff09; 什么是光线追踪 光线的基本假设 光线沿直…

王道计组(23版)5_中央处理器

CPU 功能 指令控制&#xff1a;取指、分析、执行 操作控制&#xff1a;一条指令的功能由若干操作信号组合实现 时间控制、数据加工、中断处理 基本结构 运算器 ALU&#xff1a;算术/逻辑运算 暂存寄存器&#xff1a;暂存从主存读来的数据&#xff0c;透明 ACC&#xff1a;…

【变量的解构赋值】

变量的解构赋值 1 数组的解构2 对象的解构 ES6允许按照一定模式从数组和对象中提取值&#xff0c;对变量进行赋值&#xff0c;这称为解构赋值。 1 数组的解构 <script>// 1.数组的解构const F4 [小沈阳,刘能,赵四,宋小宝];let [xiao,liu,zhao,song] F4;console.log(xi…

算法基础(三):链表知识点及题型讲解

算法基础&#xff08;三&#xff09;&#xff1a;链表知识点及题型讲解 1 链表定义2 Python链表常用操作2.1 创建链表2.2 添加元素2.3 访问元素2.4 搜索元素2.5 更新元素2.6 删除元素2.7 获取链表长度 3 力扣题目训练 一些算法基础知识点和leetcode题解&#xff0c;语言是pytho…