leetcode 所有可能的路径(图的遍历:深度优先和广度优先)

news2024/12/30 2:45:47

 leetcode 链接:

 所有可能的路径

1 图的基本概念

1.1 有向图和无向图

左边是有向图,右边是无向图。对于无向图来说,图中的边没有方向,两个节点之间只可能存在一条边,比如 0 和 1 之间的边,因为是无向图,这条边可以表示从 0 到 1 的边,也可以表示从 1 到 0 的边。对于有向图来说,图中的边有方向, 0 和 1 之间可以存在两条边,一条表示从 0 到 1 的边,一条表示从 1 到 0 的边。

用邻接矩阵来表示上边两个图,如下所示。

// 有向图
[
  [1], // 有从 0 到 1 的边
  [0, 2], // 从 1 到 0 的边和从 1 到 2 的边
  [0]  // 从 2 到 0 的边
]

// 无向图
[
  [1, 2], // 从 0 到 1 和从 0 到 2 的边
  [0, 2], // 从 1 到 0 和从 1 到 2 的边
  [0, 1]  // 从 2 到 0 和从 2 到 1 的边
]

1.2 有环图和无环图

讨论有环图还是无环图,一般说的是有向图。因为对于无向图来说,从 0 到 1 有边,那么从 1 到 0 就有边,本身就是一个环。

有环图说的是从一个节点开始遍历,在遍历过程中还能遍历到这个节点的图,除了开始节点和结束节点是相同的,其它节点不能重复出现,并且路径长度大于 2。

在图的遍历算法中,为了防止一个节点被重复遍历,往往需要一个 visited[n] 数组来标记一个节点是不是被遍历过。visited 数组下标是节点的值,元素值均被初始化为 0,当一个节点被遍历时,则将值改为 1。对于有向有环图来说,需要 visited 数组来标记,因为有环,一个节点可能被多次访问;对于有向无环图来说,一个节点不会被重复遍历,所以不需要 visited 数组来标记。

1.3 连通图和非连通图

下边两个图,上边的是非连通图,下边的是连通图。连通图,指的是从一个节点出发沿着边进行遍历,能把图中的节点都遍历到的图。 这个很像那种益智小游戏,看怎么样能一笔把图中的所有点连起来。

当对图做遍历时,如果图是连通的,那么从一个节点开始,遍历一次,就能将图中所有的节点遍历一遍,所以遍历一次就可以了。如果图不是连通的,那么遍历一次,无法将所有的点都遍历一遍,这个时候要从每个点都开始,每个节点都要开始一次,遍历一遍。

2 深度优先搜索和广度优先搜索

图是一种二维的数据结构,可以使用邻接表或者邻接矩阵来表示,更多的使用邻接表来表示,有行和列。

1.3 节中连通图的邻接矩阵表示如下:

深度优先,从遍历过程来看,优先在纵向遍历,纵深,深度。

广度优先,从遍历过程来看,优先在横向遍历,横向是广度。

纵向是深度,横向是广度。

上边这个图,从节点 0 开始遍历,第一步遍历到节点 1,下一步遍历的选择就是深度优先和广度优先的区别。下一步遍历 1 节点开始的链表,也就是跳到第二行开始遍历,遍历到节点 1 的邻接点 2,这就是深度优先遍历;下一步还是在 0 这一行,遍历节点 2,这就是广度优先遍历。

深度优先遍历和广度优先遍历不仅仅适用于图这种数据结构。二叉树的遍历包括前序遍历,中序遍历,后序遍历以及层序遍历。前 3 种遍历方式属于深度优先遍历,层序遍历属于广度优先遍历。二叉树也属于二维的数据结构。

二叉树遍历算法和应用

2.1 时间复杂度

深度优先和广度优先遍历图,都是沿着边进行遍历的。如果节点个数是 n,那么对于无向图来说,边的个数最大是 n(n - 1)/2;对有向图来说,边的个数最大是 n(n - 1)。

所以最差的情况下,两种算法的时间复杂度均是 O(n * n)。

3 所有可能的路径

如下是 leetcode 中的题目说明。

题意中的图是有向无环图,有向并且没有环,所以在遍历的时候不需要使用 visited 数据来标记一个节点是不是被访问过,因为没有环,一个节点不会被重复访问。

3.1 深度优先

这个题目适合使用深度优先遍历算法。深度优先遍历,是递归算法,递归算法需要注意两点:递归退出条件,一定要有退出条件,否则会一直递归下去;递归体,也就是递归算法的主要逻辑。递归算法的这两点与循环类似,循环算法也包括循环退出条件和循环主要逻辑。

找路径的题目,使用递归算法的题目,不管是图和是二叉树,最核心的就是如下代码注释中的三段式。

(1)将节点加入到路径

(2)递归

(3)将节点移出路径

为什么加入的节点要移出呢,因为这个节点加入之后,进行了递归运算。也就是说以这个节点为基础的路径都已经全部遍历。下一次要遍历的节点与这个节点是并列的节点,并不是路径的前后关系,它们属于这一行的邻接点。要进行下一步遍历,这个节点需要移出,因为后边遍历的节点跟这个节点不在一个路径,只不过都是当前这一行的得邻接点而已。

class Solution {
public:
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
      const int n = graph.size();
      // 没有数据,直接返回
      if (n == 0) {
        return ret;
      }
      // 要找的路径是从 o 到 n - 1
      // 所以从 0 开始遍历
      // 在遍历之前要把这个点加入到路径中
      one_path.push_back(0);

      // 深度优先遍历
      DfsScan(graph, 0, n);
      return ret;
    }
    
    // 深度优先遍历时一种递归算法
    void DfsScan(vector<vector<int>>& graph, int index, const int n) {
      // 递归退出条件,当前这个点是 n - 1 的时候,说明找到了这样一个路径
      // 将这条路径加入到结果中
      if (index == n - 1) {
        ret.push_back(one_path);
        return;
      }
   
      // 递归体,深度优先搜索
      // 对于遍历的这个节点,找到这个节点锁代表的这一行,遍历这一行
      // 这种方式取出数据的一行,会发生拷贝,直接使用引用来获取数据可以避免数据拷贝
      //   vector<int> line = graph[index];
      //   int line_size = line.size();
      //   for (int i = 0; i < line_size; i++) {
      for (int & data : graph[index]) {
        // 三段式
        // 1、push_back 将节点加入到路径中
        // 2、递归运算
        // 3、将节点从路径中移走
        one_path.push_back(data);
        DfsScan(graph, data, n);
        one_path.pop_back();
      }
    }

private:
    vector<vector<int>> ret;
    vector<int> one_path;
};

3.2 广度优先

广度优先需要使用一个队列和循环算法。在循环之前将一个元素加入到队列中,循环的条件是队列不为空,循环中的逻辑是把新遍历到的节点加入到队列中。

找路径这样的题目,广度优先没有深度优先好理解。深度优先在遍历的过程中,前后相邻的两个被遍历的点一定是一条路径上的前后的两个点,这样遍历到一个点,直接追加到路径上就可以。而广度优先遍历不是这样的,相邻两次遍历的点不是属于路径上的前后点,而是只属于当前这一行首节点的邻接点。这样就需要给每个节点维护一个路径,当这个节点是首节点的时候,那么这个节点的所有邻接点的路径都要更新,都要在这个首节点的路径的基础上加上当前这个节点。

  class Solution {
public:
    struct Node {
      int data;
      vector<int> path;
    };
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
      int size = graph.size();
      if (size == 0) {
        return ret;
      }
      
      Node node;
      node.data = 0;
      // 将节点加入到队列中
      // 节点保存着到当前这个节点的路径
      node.path.push_back(0);
      // 循环的出发条件,只要队列不是空,循环就继续进行
      q.push(node);
      while (!q.empty()) {
        // 从队列中获取一个元素
        // 开始遍历以这个元素为行首的节点,即广度优先遍历
        Node tmp = q.front();
        q.pop();
        for (int &d : graph[tmp.data]) {
            Node linenode;
            linenode.data = d;
            linenode.path = tmp.path;
            linenode.path.push_back(d);
            // 当前这个节点是 size - 1 了,找到了满足条件的一个路径
            // 那么这个节点就不需要加入队列了
            // 加入队列之后下次遍历的时候,路径后边还要加入其它节点
            // 继续追加没有意义,因为当前已经是要找的路径了
            if (d == size - 1) {
                ret.push_back(linenode.path);
            } else {
                q.push(linenode);
            }
        }
      }
      return ret;
    }

private:
  std::queue<Node> q;
  vector<vector<int>> ret;
};

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

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

相关文章

抖音 根据sec uid获取个人详细信息(性别、年龄、属地、关注数、粉丝数、简介等)

本文带来用户的secuid获取用户信息以及其他基本信息&#xff1a; 话不多说看效果&#xff1a; 第一步输入用户sec_uid 根据secuid获取到用户基本信息&#xff1a; 可以支持接口批量转换&#xff0c;欢迎咨询

从数据采集到部署,手把手带你训练一个高质量的图像分类模型

本文来自社区投稿&#xff0c;作者李剑锋 MMPreTrain 是一款基于 PyTorch 的开源深度学习预训练工具箱&#xff0c;本文将从数据采集到部署&#xff0c;手把手带大家使用 MMPreTrain 算法库训练一个高质量的图像分类模型。 MMPreTrain 项目链接&#xff1a; https://github.co…

正则表达式(Linux 下搭配 grep 使用)

目录 1.基本正则表达式 2.扩展正则表达式 3.实操之grep筛选 3.1基本正则表达式 3.2扩展正则表达式 1.基本正则表达式 ^ 用于最左端,如^"abc",匹配以abc开头的行 $ 用于最右端,如"abc$",匹配以abc结尾的行 ^$ …

使用Oracle VM VirtualBox安装Centos7

下载软件 VirtualBox下载&#xff1a; https://www.virtualbox.org/ CentOS7下载&#xff1a;http://mirrors.aliyun.com/centos/7/isos/x86_64/(阿里云镜像下载快) VirtualBox下载好之后&#xff0c;双击运行后&#xff0c;根据提示直接下一步到底就行了。 下面开始安装cent…

接连获得2项认可!细探美创信创数据安全方案与实践

信创浪潮奔涌向前&#xff0c;筑信创防线&#xff0c;守数据安全&#xff0c;近年来&#xff0c;美创科技率先布局&#xff0c;持续在信创产品类型、产品更新迭代、国产信创改造服务、解决方案、生态建设等方面创新与实践&#xff0c;以满足各行业用户数据安全建设需求&#xf…

Redis为何如此快与其线程模型

Redis是单线程的为什么如此快 ①redis是基于内存的 首先,Redis 是基于内存的数据库&#xff0c;不论读写操作都是在内存上完成的&#xff0c;完全吊打磁盘数据库的速度。 ②Redis是单线程模型&#xff0c;从而避开了多线程中上下文频繁切换的操作 Redis 的单线程指的是 Red…

(2024,选择性遗忘,积极遗忘,消极遗忘)机器学习及其他领域的“遗忘”:综述

"Forgetting" in Machine Learning and Beyond: A Survey 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0 摘要 1 引言 2 不同知识领域中的遗忘背景 2.1 心理学中的遗忘&…

responses-validator接口断言之状态码

概述 responses-validator 专用于对 reqeuests 的响应对象进行断言&#xff0c; 同时&#xff0c;为了更适用 yaml 的场景&#xff0c;支持了多种灵活、可扩展的写法&#xff0c;可用于搭建yaml接口自动化测试框架。 根据 reqeuests 响应对象的特点&#xff0c;responses-val…

电流监测利器!FP137宽共模范围高侧轨电流测量IC助您解决电流问题!

随着大量包含高精度放大器和精密匹配电阻的IC的推出&#xff0c;在高侧电流测量中使用差分放大器变得非常方便。高侧检测带动了电流检测IC的发展&#xff0c;降低了由分立器件带来的参数变化、器件数目太多等问题&#xff0c;集成电路方便了我们使用。本文将对FP137高端电流检测…

用Vue3构建一个交互式3D图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 代码相关的技术博客 应用场景介绍 本代码段用于创建可用于展示服务或产品计划的卡片式组件。此类组件常见于网站或应用程序中&#xff0c;用于以清晰简洁的方式呈现不同级别的功能和定价信息。 代码基本功能…

Pycharm将python程序打包为exe文件

前提&#xff1a;我的文件只有一个&#xff0c;多个的还没有试过 打开项目终端 s1&#xff1a;在项目终端输入&#xff08;注意要在自己的项目下&#xff09; pip install pyinstaller s2&#xff1a;在项目终端输入&#xff08;注意要在自己的项目下&#xff09; pyinstaller …

香港国际ESG联盟:绿色能源时代的先锋力量

在香港国际ESG联盟引领绿色能源革命浪潮的今天&#xff0c;ESG联盟正以矢志不渝的精神推动大陆储能市场的繁荣与进步&#xff0c;同时立足于香港这一国际金融之都&#xff0c;ESG联盟的视野早已超越地域界限&#xff0c;向中国内地乃至全球市场延伸开来。联盟始终秉承着创新开放…

大模型生成短视频

最近看到一个开源项目可以通过AI生成短视频&#xff0c;然后尝试了下&#xff0c;感觉还不错&#xff0c;下面是具体步骤。 项目名叫moneyprinterTurbo&#xff0c;它本意是对接到Youtube&#xff0c;自动生成视频并上传到Youtube获取流量赚钱&#xff0c;所以项目名叫moneypri…

图形和插图软件Canvas X Pro 20 Build 914

Canvas X Pro是一款功能强大、用途广泛的Windows软件,旨在处理技术图形和可视化,该程序结合了创建矢量和光栅图形的工具,这使其成为需要创建高质量技术插图和演示文稿的工程师、设计师、科学家和其他专业人士的理想选择。 Canvas X Pro的主要功能之一是支持处理大型和复杂的…

ssm职工健康素养数据管理系统设计与实现-计算机毕业设计源码45392

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对职工健康素养数据管理系统等问题&#xff0…

SpringBoot基础配置文件

在SpringBoot当中, 所有的配置文件都将会被配置在application.preperties当中 一: 配置服务器端口 只需要在相应的properties当中输入, 修改我们的端口号即可 server.port80 二: 修改banner #设置 将我们的Banner不再进行显示, 或者是也可以让其在控制台当中显示, 或者是日…

Python 全栈体系【四阶】(五十九)

第五章 深度学习 十三、自然语言处理&#xff08;NLP&#xff09; 4. 语言模型 4.1 什么是语言模型 语言模型在文本处理、信息检索、机器翻译、语音识别中承担这重要的任务。从通俗角度来说&#xff0c;语言模型就是通过给定的一个词语序列&#xff0c;预测下一个最可能的词…

外包SEO如何进行?

当你的业务需要外包seo时&#xff0c;首先选择一个信誉良好且经验丰富的SEO服务提供商至关重要&#xff0c;深入研究其过往案例和客户评价可以帮助你评估他们的专业性和可靠性。 在选择了合适的服务商后&#xff0c;你需要与他们进行深入的沟通&#xff0c;讨论你的业务特点、市…

信贷背后的大秘密:贷后管理的作用与智慧

很多朋友可能都听过“贷后管理”这个词&#xff0c;尤其是在征信报告的查询记录里&#xff0c;它经常是出现频率最高的。很多人可能觉得贷后管理就是走过场&#xff0c;没啥大用&#xff0c;其实&#xff0c;这背后可是大有门道的。 贷后管理到底是个啥&#xff1f; 贷后管理&a…

SpringBoot+layui实现商品打标

标题 下拉框组件效果图代码实现前端界面产品打标页面代码 后端代码controllerservice ,serviceImplmappermapper.xmlentity 数据库表 下拉框组件 xm-select 效果图 代码实现 前端界面 <script type"text/html" id"stockTags"><div><div&…