代码随想录D50-51 图论 Python

news2025/2/21 16:38:54

理论基础

理论基础部分依然沿用代码随想录教程中的介绍:

图的种类

连通性

连通性用于表示图中节点的连通情况。

如果有节点不能到达其他节点,则为非连通图,想象将多个水分子表示为图,不考虑非键作用,这张图就不是连通图。

强连通图的概念只针对有向图,因为有向图边的方向也会影响各个节点之间的连通性。

无向图中 节点3 、节点4 构成的子图不是该无向图的联通分量。因为必须是极大联通子图才能是连通分量,所以 必须是节点3、节点4、节点6 构成的子图才是连通分量。 

 图的构造

- 邻接矩阵 使用 二维数组来表示图结构。 邻接矩阵是从节点的角度来表示图,有多少节点就申请多大的二维数组。

邻接矩阵的优点:

  • 表达方式简单,易于理解

  • 检查任意两个顶点间是否存在边的操作非常快

  • 适合稠密图,在边数接近顶点数平方的图中,邻接矩阵是一种空间效率较高的表示方法。

缺点:

  • 遇到稀疏图,会导致申请过大的二维数组造成空间浪费 且遍历 边 的时候需要遍历整个n * n矩阵,造成时间浪费

- 邻接表 使用 数组 + 链表的方式来表示。 邻接表是从边的数量来表示图,有多少边 才会申请对应大小的链表。

邻接表的优点:

  • 对于稀疏图的存储,只需要存储边,空间利用率高

  • 遍历节点连接情况相对容易

缺点:

  • 检查任意两个节点间是否存在边,效率相对低,需要 O(V)时间,V表示某节点连接其他节点的数量。

  • 实现相对复杂,不易理解

图的遍历方式

图的遍历方式基本是两大类:

  • 深度优先搜索(dfs)

  • 广度优先搜索(bfs)

在讲解二叉树章节的时候,其实就已经讲过这两种遍历方式。

二叉树的递归遍历,是dfs 在二叉树上的遍历方式。

二叉树的层序遍历,是bfs 在二叉树上的遍历方式。

深度优先搜索理论基础 重要!

对比bfs(广度优先搜索来说):

  • dfs是指定一个方向去搜,不到黄河不回头,直到遇到绝境了,搜不下去了,再换方向(换方向的过程就涉及到了回溯),回到上一个节点,然后继续。

  • bfs是先把本节点所连接的所有节点遍历一遍,走到下一个节点的时候,再把连接节点的所有节点遍历一遍,搜索方向更像是广度,四面八方的搜索过程。

因此我们可以发现,深搜和广搜在图结构上是有一个范式框架的,首先学习深搜框架:

深度优先搜索的本质是回溯算法,回溯就涉及到递归。因此可以使用递归框架:

1. 确认递归函数,确认传入参数

2. 确认函数终止条件

3. 图中涉及到节点出发路径,因此要额外加上路径

98. 所有可达路径

要点:

找到从1到n的所有路径,图遍历方法的考察。输入第一行为节点数和边数量,之后为边索引。

解法:

1. 确认递归函数,参数

首先我们dfs函数一定要存一个图,用来遍历的,需要存一个目前我们遍历的节点,定义为x。

还需要存一个n,表示终点,我们遍历的时候,用来判断当 x==n 时候 标明找到了终点。

2. 确认终止条件

当目前遍历的节点 为 最后一个节点 n 的时候 就找到了 一条 从出发点到终止点的路径。

3. 处理目前搜索节点出发的路径

接下来是走,当前遍历节点x的下一个节点。这个步骤的逻辑如下:

首先 是要找到 x节点指向了哪些节点

然后 选中的x所指向的节点,加入到单一路径来。

最后 进入下一层递归

实现:


def dfs(graph, x, n, path, result):
    
    if x == n:
        ## 一定要注意使用copy 保证过程中所有路径的记录
        result.append(path.copy())
        return
    
    for i in range(1, n + 1):
        if graph[x][i] == 1:
            # 判断边之后将节点路径加入
            path.append(i)
            # 递归继续深度搜索
            dfs(graph, i, n, path, result)
            # 退出dfs说明当前路径结束 回溯
            path.pop()


def main():
    
    n, m = map(int, input().split())
    
    ## 方便直接对应节点
    graph = [[0] * (n + 1) for _ in range(n + 1)]
    
    for i in range(m):
        s, t = map(int, input().split())
        graph[s][t] = 1 
        
    result = []
    ## 启动深度优先搜索
    dfs(graph, x=1, n=n, path=[1], result=result)
    
    if not result:
        print(-1)
    
    else:
        ## 注意输出格式
        for path in result:
            print((' ').join(map(str, path)))
    

if __name__ == "__main__":
    
    main()
    

广度优先搜索理论基础 重要!

广搜的搜索方式就适合于解决两个点之间的最短路径问题

因为广搜是从起点出发,以起始点为中心一圈一圈进行搜索,一旦遇到终点,记录之前走过的节点就是一条最短路。

当然,也有一些问题是广搜 和 深搜都可以解决的,例如岛屿问题,这类问题的特征就是不涉及具体的遍历方式,只要能把相邻且相同属性的节点标记上就行

BFS是一圈一圈的搜索过程,我们用一个方格地图,假如每次搜索的方向为 上下左右(不包含斜上方),那么给出一个start起始位置,那么BFS就是从四个方向走出第一步。

搜索的直观过程如下:

在广度优先搜索中,即使有障碍,搜索也是能够继续执行的。 

99. 岛屿数量 广度优先

要点:

遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都标记上

在广度搜索函数中,我们需要指定一个优先队列deque,以保证搜索可以逐层进行。

同时,考虑搜索的剪枝,我们需要在搜索过程中严谨地维护已访问节点,以及判断可以遍历的合法节点。

实现:

from collections import deque

directions = [[0, 1], [1, 0], [-1, 0], [0, -1]]

def bfs(graph, visit, x, y):
    
    ## 初始化一个队列 执行先进先出
    que = deque([])
    ## 将当前传入节点加入队列
    que.append([x, y])
    ## 只要计算了节点 就先记录visit防止重复
    visit[x][y] = True
    
    while que:
        cur_x, cur_y = que.popleft()
        ## 遍历四个方向上的所有节点 每个节点遍历完就加入队列 等待下一层
        for i, j in directions:
            next_x, next_y = cur_x + i, cur_y + j
            ## 排除不合法节点
            if next_y < 0 or next_x < 0 or next_x >= len(graph) or next_y >= len(graph[0]):
                continue
            ## 将之前没有访问的节点进行记录 标记visit 
            if not visit[next_x][next_y] and graph[next_x][next_y] == 1:
                visit[next_x][next_y] = True
                ## 同时将该节点加入队列 进入下一层遍历
                que.append([next_x, next_y])
                

def main():
    
    n, m = map(int, input().split())
    graph = []
    ## 初始化节点分布
    for i in range(n):
        graph.append(list(map(int, input().split())))
    
    visit = [[False] * m for _ in range(n)]
    res = 0
    
    ## 逐个节点遍历 每次执行广度优先 直到队列元素周围没有岛屿
    for i in range(n):
        for j in range(m):
            if graph[i][j] == 1 and not visit[i][j]:
                res += 1
                bfs(graph, visit, i, j)
    
    print(res)

if __name__ == '__main__':
    
    main()

 99. 岛屿数量 深度优先

要点:

对于节点的访问而不是路径的访问,深搜的关联条件会多一些:

1. 确认递归函数,参数

递归函数对节点的四个方向进行遍历,如果存在符合要求的节点,就一直遍历直到一次深搜终止。此时无需pop,因为不用记录路径。回到四个方向的循环中会自动开始下一个方向的遍历,以此找完全部数值为1的节点。

2. 确认终止条件

当前节点的值为0,或者已经被遍历过,则终止并回退。

3. 处理目前搜索节点出发的路径

在外层首先做一个对所有节点的遍历,保证代入递归的节点符合岛屿+未访问的要求。一旦进入递归,就意味着我们来到了一个新的岛屿。

实现:

directions = [[0, 1], [1, 0], [-1, 0], [0, -1]]

def dfs(graph, visit, x, y):
    ## 没有岛屿或下一个节点已经被访问
    if graph[x][y] == 0 or visit[x][y]:
        return
    ## 记录当前访问的节点
    visit[x][y] = True
    ## 根据方向访问下一个节点
    for i, j in directions:
        next_x, next_y = x + i, y + j 
        # 排除不合法节点
        if next_x < 0 or next_x >= len(graph) or next_y < 0 or next_y >= len(graph[0]):
            continue
        ## 执行深度优先搜索
        dfs(graph, visit, next_x, next_y)
    

def main():
    
    n, m = map(int, input().split())
    graph = []
    ## 初始化节点分布
    for i in range(n):
        graph.append(list(map(int, input().split())))
    
    res = 0
    visit = [[False] * m for _ in range(n)]
    
    for i in range(n):
        for j in range(m):
            ## 深搜递归退出回到循环时意味着上一个岛屿一定被遍历完了
            if graph[i][j] == 1 and not visit[i][j]:
                res += 1 
                dfs(graph, visit, i, j)
    
    print(res)

99. 岛屿的最大面积 深度优先

 

要点:

本题需要比对不同岛屿之间的最大面积,言外之意就是要记录遍历过的岛屿,使用深度优先搜索时可以在dfs内部计数,更简便的技巧是定义全局变量,无参实现主函数和dfs的传递。

实现:
directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]

def dfs(graph, visit, x, y):
    global result
    if graph[x][y] == 0 or visit[x][y]:
        return
    
    visit[x][y] = True  # 先标记为访问过
    result += 1  # 计数

    for i, j in directions:
        next_x, next_y = x + i, y + j 
        if next_x < 0 or next_x >= len(graph) or next_y < 0 or next_y >= len(graph[0]):
            continue
        
        dfs(graph, visit, next_x, next_y)  # 使用next_x和next_y进行递归调用

def main():
    global result

    n, m = map(int, input().split())
    graph = []
    
    for i in range(n):
        graph.append(list(map(int, input().split())))
        
    max_res = 0
    visit = [[False] * m for _ in range(n)]
    
    for i in range(n):
        for j in range(m):
            if graph[i][j] == 1 and not visit[i][j]:
                result = 0  # 初始化为0
                dfs(graph, visit, i, j)
                max_res = max(max_res, result)
    
    print(max_res)

if __name__ == "__main__":
    main()

 99. 岛屿的最大面积 广度优先

要点:

广度优先搜索仍然遵循层级关系,使用deque记录层次;在每次遍历到新的节点时记录即可。

实现:

from collections import deque

directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]

def bfs(graph, visit, x, y):
    
    result = 1
    que = deque([])
    que.append([x, y])
    visit[x][y] = True
    
    while que:
        
        cur_x, cur_y = que.popleft()
        
        for i, j in directions:
            
            next_x, next_y = cur_x + i, cur_y + j 
            
            if next_x < 0 or next_x >= len(graph) or next_y < 0 or next_y >= len(graph[0]):
                continue
            
            if not visit[next_x][next_y] and graph[next_x][next_y] == 1:
                visit[next_x][next_y] = True
                que.append([next_x, next_y])
                result += 1
                
    return result


def main():

    n, m = map(int, input().split())
    graph = []
    
    for i in range(n):
        graph.append(list(map(int, input().split())))
        
    max_res = 0
    visit = [[False] * m for _ in range(n)]
    
    for i in range(n):
        for j in range(m):
            if graph[i][j] == 1 and not visit[i][j]:
                result = bfs(graph, visit, i, j)
                max_res = max(max_res, result)
            
    print(max_res)

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

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

相关文章

Mac M3/M4 本地部署Deepseek并集成vscode

Mac 部署 使用傻瓜集成平台ollama&#xff0c;ollama平台依赖于docker&#xff0c;Mac的M3/M4 因doesn’t have VT-X/AMD-v enabled 所以VB,VM无法使用&#xff0c;导致docker无法启动&#xff0c;需要使用docker的替代品podman&#xff0c; 它完全兼容docker brew install p…

TikTok账户安全指南:如何取消两步验证?

TikTok账户安全指南&#xff1a;如何取消两步验证&#xff1f; 在这个数字化的时代&#xff0c;保护我们的在线账户安全变得尤为重要。TikTok&#xff0c;作为全球流行的社交媒体平台&#xff0c;其账户安全更是不容忽视。两步验证作为一种增强账户安全性的措施&#xff0c;虽…

【C++复习专题】—— 类和对象,包含类的引入、访问限定符、类的6个默认成员函数等

1.类的定义 class classname {//类体&#xff1a;由成员函数和成员变量组成 }; class为定义类的关键字&#xff0c;classname为类的名字&#xff0c;{}中为类的主体。 类体中的内容称为类的成员&#xff1a;类中的变量称为类的属性或成员变量&#xff1b;类中的函数称为类的方…

Spring--BeanDefinition的用法

原文网址&#xff1a;Spring--BeanDefinition的用法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍BeanDefinition的用法。 BeanDefinition是Bean的信息&#xff0c;用于生成Bean。 示例&#xff1a;手动注册Bean 待填充 BeanDefinition的作用 get 下图是通过beanDefinitio…

关于C#的一些基础知识点汇总

1.C#结构体可以继承接口吗&#xff1f;会不会产生GC&#xff1f; 在 C# 中&#xff0c;结构体不能继承类&#xff0c;但可以实现接口。 代码&#xff1a; interface IMyInterface {void MyMethod(); }struct MyStruct : IMyInterface {public void MyMethod(){Console.Write…

一文讲解Redis为什么读写性能高以及I/O复用相关知识点

Redis为什么读写性能高呢&#xff1f; Redis 的速度⾮常快&#xff0c;单机的 Redis 就可以⽀撑每秒十几万的并发&#xff0c;性能是 MySQL 的⼏⼗倍。原因主要有⼏点&#xff1a; ①、基于内存的数据存储&#xff0c;Redis 将数据存储在内存当中&#xff0c;使得数据的读写操…

Hadoop-HA(高可用)机制

首先&#xff1a;在每个NAMENODE上都会有一个zkfc&#xff08;zookeeper failover colltroller&#xff09; &#xff0c;负责这两个的状态管理。哪个是&#xff08;active和standby&#xff09;然后写入zk集群里面。同时监控自己所在的机器是否正常。一旦active上zkfc的发现异…

51单片机-按键

1、独立按键 1.1、按键介绍 轻触开关是一种电子开关&#xff0c;使用时&#xff0c;轻轻按开关按钮就可使开关接通&#xff0c;当松开手时&#xff0c;开关断开。 1.2、独立按键原理 按键在闭合和断开时&#xff0c;触点会存在抖动现象。P2\P3\P1都是准双向IO口&#xff0c;…

深度学习的力量:精准肿瘤检测从此不再遥远

目录 引言 一、医学图像分析的挑战与深度学习的优势 1.1 医学图像分析的挑战 1.2 深度学习的优势 二、肿瘤检测的深度学习模型设计 2.1 卷积神经网络&#xff08;CNN&#xff09;的基本原理 2.2 网络架构设计 2.3 模型训练 三、肿瘤检测中的挑战与解决方案 3.1 数据不…

初尝git自结命令大全与需要理解的地方记录

常用命令 git init–初始化工作区touch 文件全称–在工作区创建文档rm 文件全称 --删除文档notepad 文件全称–在工作区打开文档cat 文件全称–在显示框显示文档的东西git status --显示工作区的文件冲突的文件 &#xff08;git add 文件全称或者.&#xff09; —将工作区文件…

LangChain 技术入门指南:探索语言模型的无限可能

在当今的技术领域&#xff0c;LangChain 正逐渐崭露头角&#xff0c;成为开发语言模型应用的强大工具。如果你渴望深入了解并掌握这一技术&#xff0c;那么就跟随本文一起开启 LangChain 的入门之旅吧&#xff01; (后续将持续输出关于LangChain的技术文章,有兴趣的同学可以关注…

Pycharm+CodeGPT+Ollama+Deepseek

首先&#xff0c;体验截图&#xff1a; 接着&#xff1a; 1、下载Ollama&#xff1a; Download Ollama on macOS 2、下载模型 以1.5b为例&#xff0c;打开命令行&#xff0c;输入: ollama run deepseek-r1:1.5b 3、Pycharm安装Code GPT插件 打开PyCharm&#xff0c;找到文…

阿里云k8s服务部署操作一指禅

文章目录 DockerFile镜像操作阿里云k8s服务部署 DockerFile # 使用 JDK 17 官方镜像 # linux架构&#xff1a;FROM --platformlinux/amd64 openjdk:17-jdk-slim # arm架构&#xff1a;openjdk:17-jdk-slim FROM --platformlinux/amd64 openjdk:17-jdk-slim# 设置工作目录 WORK…

.NET + Vue3 的前后端项目在IIS的发布

目录 一、发布准备 1、安装 IIS 2、安装 Windows Hosting Bundle&#xff08;.NET Core 托管捆绑包&#xff09; 3、安装 IIS URL Rewrite 二、项目发布 1、后端项目发布 2、前端项目发布 3、将项目部署到 IIS中 三、网站配置 1、IP配置 2、防火墙配置 3、跨域配置…

交互编程工具之——Jupyter

Jupyter 是什么&#xff1f; Jupyter 是一个开源的交互式编程和数据分析工具&#xff0c;广泛应用于数据科学、机器学习、教育和研究领域。其核心是 Jupyter Notebook&#xff08;现升级为 JupyterLab&#xff09;&#xff0c;允许用户在一个基于浏览器的界面中编写代码、运行…

微信小程序客服消息接收不到微信的回调

微信小程序客服消息&#xff0c;可以接收到用户进入会话事件的回调&#xff0c;但是接收不到用户发送消息的回调接口。需要在微信公众平台&#xff0c;把转发消息给客服的开关关闭。需要把这个开关关闭&#xff0c;否则消息会直接发送给设置的客服&#xff0c;并不会走设置的回…

easyexcel 2.2.6版本导出excel模板时,标题带下拉框及其下拉值过多不显示问题

需求背景&#xff1a;有一个需求要做下拉框的值有100多条&#xff0c;同时这个excel是一个多sheet的导入模板 直接用easyexcel 导出&#xff0c;会出现下拉框的值过多&#xff0c;导致生成出来的excel模板无法正常展示下拉功能 使用的easyexcel版本&#xff1a;<depende…

影视大数据分析新范式:亮数据动态代理驱动的实时数据采集方案

一、项目背景与挑战 在数据驱动决策的时代&#xff0c;影视数据分析对内容平台至关重要。但豆瓣等平台设有&#xff1a; 高频请求IP封禁机制User-Agent指纹检测请求频率阈值控制验证码验证系统 传统爬虫方案面临&#xff1a; 单一IP存活时间<5分钟采集成功率<30%数据更新…

免费体验,在阿里云平台零门槛调用满血版DeepSeek-R1模型

一、引言 随着人工智能技术的飞速发展&#xff0c;各类AI模型层出不穷。其中&#xff0c;DeepSeek作为一款新兴的推理模型&#xff0c;凭借其强大的技术实力和广泛的应用场景&#xff0c;逐渐在市场中崭露头角。本文将基于阿里云提供的零门槛解决方案&#xff0c;对DeepSeek模…

Cursor 与团队协作:提升团队开发效率

引言 在团队开发中&#xff0c;代码质量参差不齐、重复错误频发、代码审查耗时过长是制约效率的三大痛点。据 GitHub 调查&#xff0c;开发者平均每周花费 4.3 小时修复他人代码问题&#xff0c;而 60% 的合并请求&#xff08;PR&#xff09;因风格或低级错误被驳回。Cursor 作…