算法-图BFS/DFS-单词接龙

算法-图BFS/DFS-单词接龙

1 题目概述

1.1 题目出处

https://leetcode-cn.com/problems/number-of-islands

1.2 题目描述

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:

如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:

输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]

输出: 5

解释: 一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
返回它的长度 5。
示例 2:

输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]

输出: 0

解释: endWord “cog” 不在字典中,所以无法进行转换。

2 图-DFS

2.1 思路

将岛屿分布看做有向图,深度遍历该节点的上下左右四个方向。

遍历到一个为’1’的节点时,标记为’0’代表已经遍历过下次不再遍历。

2.2 代码

class Solution {public int numIslands(char[][] grid) {int result = 0;if(grid == null || grid.length == 0){return result;}for(int i = 0; i < grid.length; i++){char[] columns = grid[i];for(int j = 0; j < columns.length; j++){char c = columns[j];if(c == '1'){dfs(grid, i, j);result++;}}}return result;}private void dfs(char[][] grid, int row, int column){char[] columns = grid[row];char c = columns[column];if(c == '0'){return;}else{grid[row][column] = '0';if(column - 1 > -1){dfs(grid, row, column - 1);}if(column + 1 < columns.length){dfs(grid, row, column + 1);}if(row + 1 < grid.length){dfs(grid, row + 1, column);}if(row - 1 > -1){dfs(grid, row - 1, column);}}}
}

2.3 时间复杂度

在这里插入图片描述
O(N*M)

  • 其中 N 和 M 分别为行数和列数。

2.4 空间复杂度

O(N*M)

  • 因为需要递归

3 BFS

3.1 思路

将岛屿分布看做有向图,遍历开始后,从当前节点广度优先遍历。

3.2 代码

class Solution {private Set<String> recordSet = new HashSet<>();public int numIslands(char[][] grid) {int result = 0;if(grid == null || grid.length == 0){return result;}for(int i = 0; i < grid.length; i++){char[] columns = grid[i];for(int j = 0; j < columns.length; j++){char c = columns[j];if(c == '1'){bfs(grid, i, j);result++;}}}return result;}private void bfs(char[][] grid, int row, int column){char[] columns = grid[row];char c = columns[column];if(c == '0'){return;}LinkedList<int[]> coordinateQueue = new LinkedList<>();coordinateQueue.add(new int[]{row, column});// bfswhile(!coordinateQueue.isEmpty()){int[] coordinate = coordinateQueue.poll();int i = coordinate[0];int j = coordinate[1];if(grid[i][j] == '0'){continue;}grid[i][j] = '0';if(j - 1 > -1){coordinateQueue.add(new int[]{i, j - 1});}if(j + 1 < columns.length){coordinateQueue.add(new int[]{i, j + 1});}if(i + 1 < grid.length){coordinateQueue.add(new int[]{i + 1, j});}if(i - 1 > -1){coordinateQueue.add(new int[]{ i - 1, j});}}}
}

3.3 时间复杂度

在这里插入图片描述
O(N*M)

  • 其中 N 和 M 分别为行数和列数。

3.4 空间复杂度

O(min(M,N))

  • 最坏情况全是岛屿

4 效率很低的第一版并查集

4.1 思路

做并查集,遇到相邻的’1’节点就合并成一个并查集。

最后返回不同的并查集数。

4.2 代码

class Solution {public int numIslands(char[][] grid) {int result = 0;if(grid == null || grid.length == 0){return result;}int[] unionFindSet = new int[grid.length * grid[0].length];for(int i = 0; i < grid.length; i++){char[] columns = grid[i];for(int j = 0; j < columns.length; j++){char c = columns[j];if(c == '1'){// 初始化岛节点的并查集为index+1find(unionFindSet, i * columns.length + j);// 连接右侧节点if(j + 1 < columns.length && grid[i][j + 1] == '1'){// 这里需要将二维数组转为一位数组的下标// 所以使用 当前行*列总数得到该行在一位数组中的首个下标,// 再加上当前列作为偏移数得到在一维数组的下标union(unionFindSet, i * columns.length + j, i * columns.length + j + 1);}// 连接下侧节点if(i + 1 < grid.length && grid[i+1][j] == '1'){union(unionFindSet, i * columns.length + j, (i + 1) * columns.length + j);}}}}Set<Integer> filter = new HashSet<>();for(int i : unionFindSet){if(i != 0){filter.add(i);}}return filter.size();}private void union(int[] unionFindSet, int p, int q){int left = find(unionFindSet, p);int right = find(unionFindSet, q);if(left == right){return;}// 查找出所有和右边元素同一个并查集元素,和左边合并for(int i = 0; i < unionFindSet.length; i++){if(unionFindSet[i] == right){unionFindSet[i] = left;}}}private int find(int[] unionFindSet, int p){if(unionFindSet[p] == 0){unionFindSet[p] = p + 1;}return unionFindSet[p];}
}

4.3 时间复杂度

在这里插入图片描述

5 并查集-优化

5.1 思路

合并两个不同祖先的节点时,将他们的祖先合并为一个即可。

最后遍历计算出不同的祖先数作为结果返回。

在union的时候还采用了压缩路径优化方法,提升查找效率。

5.2 代码

class Solution {public int numIslands(char[][] grid) {int result = 0;if(grid == null || grid.length == 0){return result;}int[] unionFindSet = new int[grid.length * grid[0].length];// 初始化所有节点的并查集,父节点设为自己for(int i = 0; i < grid.length; i++){char[] columns = grid[i];for(int j = 0; j < columns.length; j++){unionFindSet[i * columns.length + j] = i * columns.length + j;}}// 下面开始合并岛节点for(int i = 0; i < grid.length; i++){char[] columns = grid[i];for(int j = 0; j < columns.length; j++){char c = columns[j];if(c == '1'){// 初始化岛节点的并查集为index+1// find(unionFindSet, i * columns.length + j);// 连接右侧节点if(j + 1 < columns.length && grid[i][j + 1] == '1'){// 这里需要将二维数组转为一位数组的下标// 所以使用 当前行*列总数得到该行在一位数组中的首个下标,// 再加上当前列作为偏移数得到在一维数组的下标union(unionFindSet, i * columns.length + j, i * columns.length + j + 1);}// 连接下侧节点if(i + 1 < grid.length && grid[i+1][j] == '1'){union(unionFindSet, i * columns.length + j, (i + 1) * columns.length + j);}}else{// 海洋节点,将他们的父节点设为-1,不参与累计unionFindSet[i * columns.length + j] = -1;}}}// 去重根节点Set<Integer> filter = new HashSet<>();// 将所有父节点不为-1的节点全部取出,并寻找他们的父节点// 只要父节点不为-1就放入过滤器统计for(int i = 0; i < unionFindSet.length; i++){if(unionFindSet[i] == -1){continue;}int root = find(unionFindSet, i);if(root > -1){filter.add(root);}}// 最终返回不重复的根节点数return filter.size();}private void union(int[] unionFindSet, int p, int q){int left = find(unionFindSet, p);int right = find(unionFindSet, q);// 说明本来就在一个并查集内,不用处理if(left == right){return;}// 将右边元素的老祖先作为左边元素老祖先的父节点,实现联通unionFindSet[left] = right;}private int find(int[] unionFindSet, int p){int son = p;// 寻找祖先根节点while(p != unionFindSet[p]){p = unionFindSet[p];}// 路径压缩优化,将当前节点及祖先节点的父节点都设为祖先根节点// 即将高度压缩为2,方便查找while(son != p){int tmp = unionFindSet[son];unionFindSet[son] = p;son = tmp;}return p;}    
}

5.3 时间复杂度

在这里插入图片描述
参考岛屿数量

5.4 空间复杂度

O(M*N)

参考文档

  • 算法-并查集

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

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

相关文章

C++八股记录

C内存管理 C中&#xff0c;内存分成5个区。 栈&#xff1a;函数内局部变量&#xff1b;自动管理&#xff0c;效率高&#xff0c;但空间较小&#xff1b; 堆&#xff1a;new分配的内存块&#xff1b;手动管理&#xff0c;效率低&#xff0c;但空间大&#xff1b; 自由存储区&…

Linux 常用命令大全

一、基本操作命令 //------首先先来几个热键&#xff0c;非常方便&#xff0c;一定要记住------// Tab ------按键—命令补齐功能Ctrlc ------按键—停掉正在运行的程序Ctrld ------按键—相当于exit&#xff0c;退出Ctrll ------按键—清屏 1.1 关机和重启 1. 关机命令&am…

弯道超车必做好题集锦三(C语言选择题)

前言&#xff1a; 编程想要学的好&#xff0c;刷题少不了&#xff0c;我们不仅要多刷题&#xff0c;还要刷好题&#xff01;为此我开启了一个弯道超车必做好题锦集的系列&#xff0c;每篇大约10题左右。此为第三篇选择题篇&#xff0c;该系列会不定期更新&#xff0c;后续还会…

全面解析MES系统中的报工操作

一、报工操作的定义&#xff1a; 报工操作是指在生产过程中&#xff0c;操作员通过MES系统记录和提交生产工序的相关信息&#xff0c;如工时、产量、质量等。报工操作将生产过程中的实际情况反馈给MES系统&#xff0c;实现生产数据的实时采集和记录。 二、报工操作的流程&…

MacOS goland go1.21 debug问题

安装dlv brew install dlv 安装之后在终端会显示所在目录 类似/usr/local/Cellar/delve/1.21.0/bin 配置goland 在文件系统中找到goland 右击选择show package contents -> Contents -> plugins -> go 尝试替换 其中对应系统 的 dlv 结果还是不行 然后打开应用gol…

shell脚本:在curl命令中传递变量、单引号 ‘ 和双引号 “区别、时间戳获取、生成UUID

在curl命令中传递变量 在curl中引用变量时要加个转义 “” 如&#xff1a; #!/bin/sh timestamp$(($(date %s%N)/1000000)) curl http://xx -H "Content-Type:application/json" -X POST -d {"timestamp": ""${timestamp}""}单引…

常见前端面试之VUE面试题汇总七

20. 对 vue 设计原则的理解 1.渐进式 JavaScript 框架&#xff1a;与其它大型框架不同的是&#xff0c;Vue 被设计 为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上 手&#xff0c;还便于与第三方库或既有项目整合。另一方面&#xff0c;当与现代化的…

universal robot 机械臂 官方基本教程

https://academy.universal-robots.cn/modules/e-Series-core-track/Chinese/module3/story_html5.html?courseId2166&languageChinese 教程1 控制箱内部 包含&#xff1a; 主机板&#xff0c;SD卡&#xff0c;和安全控制板 安全控制板负责所有控制信息&#xff0c;包括…

Ansible学习笔记(持续更新)

Ansible学习目录 1.自动化运维1.1 企业实际应用场景1.1.1 Dev开发环境1.1.2 测试环境1.1.3 发布环境1.1.4 生产环境1.1.5 灰度环境 1.2 程序发布1.3 自动化运维应用场景1.4 常用自动化运维工具 2.Ansible介绍和架构2.1 Ansible特性2.2 Ansible架构2.2.1 Ansible主要组成部分2.2…

LeetCode(力扣)617. 合并二叉树Python

LeetCode617. 合并二叉树 题目链接代码 题目链接 https://leetcode.cn/problems/merge-two-binary-trees/ 代码 递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # …

c# 使用了 await、asnync task.run 三者结合使用

在 C# 异步编程中&#xff0c;await 和 async 关键字结合使用可以让你更方便地编写异步代码&#xff0c;而无需直接使用 Task.Run。然而&#xff0c;有时候你可能仍然需要使用 Task.Run 来在后台线程上执行某些工作&#xff0c;这取决于你的代码逻辑和需求。 await 和 async 关…

【springboot】springboot定时任务:

文章目录 一、文档&#xff1a;二、案例&#xff1a; 一、文档&#xff1a; 【cron表达式在线生成器】https://cron.qqe2.com/ 二、案例&#xff1a; EnableScheduling //开启任务调度package com.sky.task;import com.sky.entity.Orders; import com.sky.mapper.OrderMapper; …

LeetCode1049. 最后一块石头的重量 II

1049. 最后一块石头的重量 II 文章目录 [1049. 最后一块石头的重量 II](https://leetcode.cn/problems/last-stone-weight-ii/)一、题目二、题解方法一&#xff1a;01背包二维数组算法思路具体实现 方法二&#xff1a;01背包一维数组 一、题目 有一堆石头&#xff0c;用整数数…

LeetCode-160. 相交链表

这是一道真的非常巧妙的题&#xff0c;题解思路如下&#xff1a; 如果让他们尾端队齐&#xff0c;那么从后面遍历就会很快找到第一个相交的点。但是逆序很麻烦。 于是有一个巧妙的思路诞生了&#xff0c;如果让短的先走完自己的再走长的&#xff0c;长的走完走短的&#xff0c;…

MyBatisx代码生成

MyBatisx代码生成 1.创建数据库表 CREATE TABLE sys_good (good_id int(11) NOT NULL,good_name varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,good_desc varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,PRIMARY KEY (good_id) ) ENGINEInnoDB DEFAULT CHA…

Multisim软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 Multisim软件是一款电路仿真和设计软件&#xff0c;由美国国家仪器公司&#xff08;National Instruments&#xff09;开发。它提供了一个交互式的图形界面&#xff0c;使用户能够轻松地构建和仿真电路。以下是Multisim软件的详…

Java中文件的创建(三种方式),文件常用的方法

文件的创建 方式1&#xff1a; new File(String pathName) 根据路径构建一个File对象方式2&#xff1a; new File(File parent,String child) 根据父目录文件子路径构建方式3&#xff1a; new File(String parent,String child) 根据父目录子路径构建 代码&#xff1a; //方…

Unity——协程(Coroutine)

本文为问GPT所得 一、在Unity中&#xff0c;协程到底是个啥 在Unity中&#xff0c;协程&#xff08;Coroutine&#xff09;是一种特殊的函数&#xff0c;用于在一段时间内暂停执行&#xff0c;并在稍后的时间点继续执行。通常情况下&#xff0c;我们在代码中通过调用协程来实现…

Android 之 传感器专题 (4) —— 其他传感器了解

本节引言&#xff1a; 在上一节的结尾说了&#xff0c;传感器部分因为笔者没怎么玩过&#xff0c;本节就简单的把剩下的几个常用的 传感器介绍一遍&#xff0c;当作科普&#xff0c;以后用到再慢慢研究~ 1.磁场传感器(Magnetic field sensor) 作用&#xff1a;该传感器主要用…

spring中LocalDateTime 转成字符串的时候注意事项

ApiOperation("查询课程发布信息") ResponseBody GetMapping("/r/coursepublish/{courseId}") public CoursePublish getCoursepublish(PathVariable("courseId") Long courseId) { CoursePublish coursePublish coursePublishService.getC…