数据结构与算法 | 图(Graph)

图的分类(Types Of Graph)

可以看到图的基本的结构非常简单,约束也很少,如果在其中加上各种条件约束就可以定义各种类型的图。

  • 约束边或者顶点个数来分类:
    • 零图(Null graph):只有顶点没有边的图;
    • 平凡图(Trivial graph):只有一个顶点的图;
  • 按照边是否有指向来分类:
    • 有向图(Directed Graph):在每个边的定义中,节点都是有序的对。也就是(A,B)与(B,A)表示不同的边,一个代表从A到B方向的边,一个代表从B到A方向的边。
    • 无向图(Undirected Graph):边只是代表链接,没有指向性。(A,B)与(B,A)表示的同样的边。
  • 根据是否在边上存储数据分类:
    • 权重图(Weighted Graph):图中的边上附加了权重或值的图。这些权重表示连接两个节点之间的距离、代价、容量或其他度量。权重可以是任何数值,通常用于描述节点间的关系特性。

还有很多分类在此不一一罗列。每类图可能还会有其独特的一些特征描述,比如有向图(Directed Graph)里面,以某顶点作为开始的边的数量称为这个顶点的入度(Indegree),以某个顶点作为结束的边的数量称为这个顶点的出度(Outdegree)等等。

通过以上描述,可以感受到图其实是非常灵活的数据结构,同时它的衍生概念也非常多;初次探究大可不必一一记牢,有个基本的图结构知识体系即可,后续遇到的时候再扩充图的知识体系更为合适。

请在此添加图片描述

图的表达(Representation of Graphs)

图的表达其实也有多种形式,不过最基本的形式是:邻接矩阵(Adjacency Matrix) 与 邻接表(Adjacency List)

邻接矩阵(Adjacency Matrix)

邻接矩阵,所谓“矩阵”具体到代码其实就是二维数组,通过二维数组来表示图中顶点之间的边的关系。二维数组中的行和列分别代表图中的顶点,矩阵中的值表示顶点之间是否相连或连接的边的权重。

且用这种方式来表示先前示例的图结构,矩阵的值 0代表无相连边,1代表有相连边。如下:

请在此添加图片描述

邻接表(Adjacency List)

邻接表,所谓“表”指的就是列表 List ,图中的每个节点都有一个对应的列表,用于存储与该节点直接相连的其他节点的信息。邻接表中的每个节点列表包含了该节点相邻节点的标识符或指针等信息。对于无权图,通常使用数组或链表来存储相邻节点的标识符。而对于带权图,列表中可能还包含了边的权重信息。

请在此添加图片描述

基本应用示例(Basic Examples)


Leetcode 997. 找到小镇的法官【简单】

小镇里有 n 个人,按从 1 到 n 的顺序编号。传言称,这些人中有一个暗地里是小镇法官。
如果小镇法官真的存在,那么:
小镇法官不会信任任何人。
每个人(除了小镇法官)都信任这位小镇法官。
只有一个人同时满足属性 1 和属性 2 。
给你一个数组 trust ,其中 trusti = ai, bi 表示编号为 ai 的人信任编号为 bi 的人。
如果小镇法官存在并且可以确定他的身份,请返回该法官的编号;否则,返回 -1 。

示例

输入:n = 2, trust = [1,2]
输出:2


题目故事背景描述比较多,可以看到 信任的表述 可以用有向图来表示,每个人 用顶点 来表示,小镇法官的第1点 代表就是出度为 0,第2点 代表就是 入度为 n-1。 这样题目就转换为:判断一个n个顶点的有向图中 是否存在出度为0,入度为n-1的顶点 ;存在返回顶点编号,不存在返回 -1。

PS:关键点,将复杂描述的题目,建模成为图

public int findJudge(int n, int[][] trust) {
        
        int[] outDegree = new int[n+1],inDegree = new int[n+1];
        
        for(int i = 0; i < trust.length; i++){
            outDegree[trust[i][0]] ++;
            inDegree[trust[i][1]]++;
        }

        for(int i=1; i<= n; i++)
            if(outDegree[i] == 0 && inDegree[i] == (n-1))
                return i;
        
        return -1;
    }

Leetcode 787. K 站中转内最便宜的航班【中等】

有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flightsi = fromi, toi, pricei ,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。
现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst 的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1。

示例

输入: n = 3, edges = [0,1,100,1,2,100,0,2,500],src = 0, dst = 2, k = 1
输出: 200
备注:1 <= n <= 100,航班没有重复,且不存在自环


将城市看作是顶点,城市-城市之间的航班看作是 有向图边,航班的价格作为边的权重,也就完成了题意到图的建模。考虑到,城市数量 n < 100, 因此可以采用 邻接矩阵的方式来进行图的表达。

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        
        // 图 初始化建模
        int[][] map = new int[n][n];
        for(int i = 0; i < flights.length; i++){
            map[flights[i][0]][flights[i][1]] = flights[i][2];
        }

        // 其他逻辑
}

以 src 作为 源顶点,通过以 src作为 起始顶点的边 链接到更多的顶点(此时经过 0个站中转);以这些链接到的顶点 为起始点,继续链接到更多的顶点(经过 1个站中转);继而可以推导到 经过 n 个站中转。这也就是典型的广度优先搜索(BFS),来遍历以src作为 源顶点的图,遍历代码如下:

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {

        // ...
        // BFS
        Deque<Integer> que = new ArrayDeque<>();
        // src 作为起始点
        que.offer(src); 

        // 经过 k 个中转站     
        for(int i = 0; i <= k && !que.isEmpty(); i++){

            int size = que.size();  
            while( size-- > 0){
                
                int  node = que.poll();
                for(int j = 0; j < map[node].length; j++){
                    
                    // map[node][j] == 0 代表 node -> 不相连跳过
                    if( map[node][j] == 0) continue;

                    // ... 这里可以加入遍历过程中更多的逻辑
                    
                    // 进入下一轮遍历
                    que.offer(j);
                }
            }
        }

        // ...
    }

考虑题目需要的是 最多经过 k 站中转的 最便宜线路,不妨 广度优先遍历中 用 distSet[] 记录下 src 可到达站点的 最低价格;最后返回 distSet[ dst ] 即可, 这里注意下的是 如果没到达,按照题意应返回 -1

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        
        // ...
        int[] distSet = new int[n];
   
        que.offer(src);  
        for(int i = 0; i <= k && !que.isEmpty(); i++){

            // 判断当前最小的 标准 是基于上一轮的遍历结果
            int[] pre = Arrays.copyOf(distSet,distSet.length);
            int size = que.size();
            
            while( size-- > 0){
                
                int  node = que.poll();
                for(int j = 0; j < map[node].length; j++){
                    
                    if( map[node][j] == 0) continue;

                    // distSet[j] == 0 代表之前没有到达过,因此需要 写入 distSet[j]
                    // 如果当前距离 不之前大,这个顶点不必进行下一轮遍历
                    if( distSet[j] != 0 && 
                        distSet[j] < pre[node] + map[node][j]) continue;
                    // 记录最小结果
                    distSet[j] = pre[node] + map[node][j] ;
                    
                    que.offer(j);
                }
            }
        }
        // distSet[j] == 0 代表之前没有到达过,返回 -1
        return distSet[dst] == 0 ? -1:distSet[dst];
    }

这里其实是 使用 Bellman-Ford 算法的思想进行解题;在图算法领域还有着很多著名的算法,后续可以整理下更专业的解读,这里只是演示个简单的应用。

Bellman-Ford 算法,最初由Alfonso Shimbel 1955年提出,但以 Richard Bellman 和 Lester Ford Jr.的名字命名,他们分别于 1958年 和 1956年 发表了该算法,向前辈致敬。

最后附上完整代码:

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        
        int[][] map = new int[n][n];
        for(int i = 0; i < flights.length; i++){
            map[flights[i][0]][flights[i][1]] = flights[i][2];
        }

        int[] distSet = new int[n];
        Deque<Integer> que = new ArrayDeque<>();
        
        que.offer(src);  
        for(int i = 0; i <= k && !que.isEmpty(); i++){

            int[] pre = Arrays.copyOf(distSet,distSet.length);
            int size = que.size();
            
            while( size-- > 0){
                
                int  node = que.poll();
                for(int j = 0; j < map[node].length; j++){
                    
                    if( map[node][j] == 0) continue;
                    if( distSet[j] != 0 && 
                        distSet[j] < pre[node] + map[node][j]) continue;

                    distSet[j] = pre[node] + map[node][j] ;
                    que.offer(j);
                }
            }
        }

        return distSet[dst] == 0 ? -1:distSet[dst];
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

指令系统、流水线

指令系统 分类 寻址方式 设计 能够改变控制流的指令&#xff1a;分支、跳转、过程调用、过程返回 操作码设计 MIPS 流水线 MIPS流水线 改进后 取指&#xff08;IF&#xff09; 译码&#xff08;ID&#xff09; 执行&#xff08;EX&#xff09; 存储器访问 寄存器-寄存器 A…

LabVIEW如何获取波形图上游标所在位置的数值

LabVIEW如何获取波形图上游标所在位置的数值 获取游标所在位置数值的一种方法是利用波形图的游标列表属性。 在VI的程序框图中&#xff0c;右键单击波形图并选择创建引用 &#xff0c;然后将创建的引用节点放在程序框图上。 在程序框图上放置一个属性节点&#xff0c;并将其…

Java制作俄罗斯方块

Java俄罗斯方块小游戏 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; imp…

open3d ICP 配准

文章目录 Three common registration techniquesPoint-to-point techniquePoint-to-plane registration ICP registrationHelper visualization functionInputGlobal registrationExtract geometric featureInputRANSAC Point-to-point ICPPoint-to-plane ICP References Three…

搭建Android自动化python+appium环境

一. 需要软件 JDK:JAVA安装后配置JDK环境 SDK:SDK下载后配置adb环境 Python:pyhton语言 Pycharm:python脚本编译工具 Appium-python-client:pyhton中的库Appium客户端 二. 搭建步骤 1.配置JDK环境 ①. 下载安装java: https://www.oracle.com/java/technologies/javase-j…

语音特征提取: 梅尔频谱(Mel-spectrogram)与梅尔倒频系数(MFCCS)

1 核心概念 1.1 语音信号 语音信号是一个非平稳的时变信号&#xff0c;但语音信号是由声门的激励脉冲通过声道形成的&#xff0c;经过声道(人的三腔&#xff0c;咽口鼻)的调制&#xff0c;最后由口唇辐射而出。认为“短时间”(帧长/窗长&#xff1a;10~30ms)内语音信号是平稳…

Unity中Shader法线贴图(下)理论篇

文章目录 前言一、采样出错的原因二、切线空间是什么&#xff1f;切线空间图解&#xff1a; 三、计算方式1、统一变换到切线空间下进行计算2、统一变换到世界空间下进行计算 四、一般统一变换到世界空间下的坐标进行计算1、求M^-1^2、求出n~w~ 前言 这篇文章&#xff0c;主要解…

【Kettle实战】字符串处理及网络请求JSON格式处理

经过大量的kettle操作实践&#xff0c;我们会渐渐掌握一些技巧&#xff0c;大大减轻清洗的工作量。比如在哪里 处理字符串更方便&#xff0c;在哪儿处理更合理都是一个取舍问题。 字符串拼接 MySQL中使用concat(字段1,字段2)&#xff0c;但是如果“字段2”为NULL&#xff0c;结…

如何挖掘xss漏洞

如何挖掘xss漏洞 对于如何去挖掘一个xss漏洞我是这样理解的 在实战情况下不能一上来就使用xss语句来进行测试很容易被发现 那这种情况该怎么办呢 打开准备渗透测试的web网站&#xff0c;寻找可以收集用户输入的地方比如搜索框&#xff0c;url框等 发现后寻找注入点 选在输入…

【Q1—45min】

1.epoll除了边沿触发还有什么&#xff1f;与select区别. epoll 是Linux平台下的一种特有的多路复用IO实现方式&#xff0c;与传统的 select 相比&#xff0c;epoll 在性能上有很大的提升。 epoll是一种当文件描述符的内核缓冲区非空的时候&#xff0c;发出可读信号进行通知&…

Find My蓝牙耳机|苹果Find My技术与耳机结合,智能防丢,全球定位

蓝牙耳机就是将蓝牙技术应用在免持耳机上&#xff0c;让使用者可以免除恼人电线的牵绊&#xff0c;自在地以各种方式轻松通话。自从蓝牙耳机问世以来&#xff0c;一直是行动商务族提升效率的好工具。正是应为蓝牙耳机小巧无线&#xff0c;人们越来越喜欢随身携带蓝牙耳机出门&a…

人民网_领导留言板data2022年-2023年

人民网_领导留言板data_2022年全年-2023年11月数据_全国任意城市 包含且不限于&#xff1a;留言ID,留言对象,留言标题,种类名,领域名,目前状态,留言日期,留言内容,回复机构,回复时间,回复内容,满意度,解决力度,沟通态度,办理时效 对于有需要爬取领导留言板的朋友&#xff0c;…

【Qt开发流程之】布局管理

介绍 一个界面呈现&#xff0c;如果要让用户有更好的观感&#xff0c;布局必不可少。 【Qt之布局】QVBoxLayout、QHBoxLayout、QGridLayout、QFormLayout介绍及使用 链接: https://blog.csdn.net/MrHHHHHH/article/details/133915208 qt布局类图&#xff1a; Qt布局是Qt图形…

echarts的使用

1. 普通版 其实主要就是option1&#xff0c;option1就是画的图 echats不能响应刷新&#xff0c;要想实时刷新监听刷新的值重新调用一下方法即可 html <div class"echart" style"width: 100%;height: calc(100% - 130px)" ref"main1">&l…

生物医药行业密钥管理系统特点 安当加密

生物医药行业密钥管理系统的特点主要表现在以下几个方面&#xff1a; 安全性&#xff1a;生物医药行业涉及的数据往往具有极高的私密性和敏感性&#xff0c;因此&#xff0c;密钥管理系统必须具备极高的安全性。这包括对密钥的生成、存储、传输和使用等各个环节进行严格的管理和…

广州一母婴店因设置0元购导致关店

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 广州的一家母婴用品网店Minitutu因双十一优惠券设置错误&#xff0c;导致所有商品变成0元购买&#xff0c;引发消费者疯狂抢购&#xff0c;15万多单订单中有800多万元的损失。店家无奈之下只能暂停营…

亚马逊,shopee,lazada自养号测评:提高店铺曝光,增加产品销量

如何在较短的时间内让自己的店铺排名升高&#xff0c;提高产品销量&#xff0c;除了依靠选品和广告之外&#xff0c;亚马逊测评 在店铺的运营中也是必不可少的环节。 自养号测评对亚马逊卖家来说&#xff0c;是运营店铺的重要手段之一。一个产品想要有更好的曝光、更高的转化率…

优卡特脸爱云一脸通智慧管理平台权限绕过漏洞复现【CVE-2023-6099】

优卡特脸爱云一脸通智慧管理平台权限绕过漏洞复现【CVE-2023-6099】 一、 产品简介二、 漏洞概述三、 影响范围四、 复现环境五、 漏洞复现手动复现自动化复现(小龙POC开源) 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信…

OSCP系列靶场-Esay-DC-1

目录 总结 准备工作 信息收集-端口扫描 目标开放端口收集 目标端口对应服务探测 信息收集-端口测试 22-SSH端口的信息收集 22-SSH端口版本信息与MSF利用(pass) 22-SSH手动登录尝试(失败) 22-SSH弱口令爆破(爆破着玩) 80-HTTP端口的信息收集 信息收集-网站指纹 漏洞…

水库大坝安全监测系统守护水利工程安全的坚实后盾

WX-WY1 随着社会经济的发展和科技的进步&#xff0c;水利工程的安全问题越来越受到人们的关注。水库大坝作为水利工程的重要组成部分&#xff0c;其安全状况直接关系到周边地区人民的生命财产安全和生态环境。因此&#xff0c;建立一个高效、可靠的水库大坝安全监测系统至关重要…