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

news2024/9/22 2:26:31

算法-图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});
        // bfs
        while(!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+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);
                    }
                }
            }
        }
        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.coloradmin.cn/o/936468.html

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

相关文章

2023年6月GESP C++ 三级试卷解析

2023年6月GESP C 三级试卷解析 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 1.高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在计算机上运行的可执行代码。 A.编辑 B.保存 C.调试 D.编译 【答案】D 【考纲知识点…

LeetCode-406-根据身高重建队列

题目描述&#xff1a; 假设有打乱顺序的一群人站成一个队列&#xff0c;数组 people 表示队列中一些人的属性&#xff08;不一定按顺序&#xff09;。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi &#xff0c;前面 正好 有 ki 个身高大于或等于 hi 的人。 请你重新构造…

IDEA项目启动的时候找不到类

IDEA项目启动的时候找不到类 我在运行微服务的项目的时候启动多个项目由于存在依赖关系&#xff0c;但是我确实是引入了对应的依赖的地址但是就是找不到对应的类。 解决的方法&#xff1a;

代码随想录算法训练营第四十八天|LeetCode 583,72,编辑距离总结篇

目录 LeetCode 583.两个字符串的删除操作 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历方向 5.打印dp数组 LeetCode 72.编辑距离 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历方…

模2运算规则

模2加法 模2加法没有进位&#xff0c;等同于异或运算。一位数的模2加法规则如下&#xff1a; 0 0 0 0 1 1 1 0 1 1 1 0 多位数的模2加法中&#xff0c;每一位都按照上面的规则进行&#xff0c;例如: 当多个数相加&#xff0c;对应位置上如果有偶数个1&#xff0c;…

【OpenCV实战】3.OpenCV颜色空间实战

OpenCV颜色空间实战 〇、Coding实战内容一、imread1.1 函数介绍1.2 Flags1.3 Code 二. 色彩空间2.1 获取单色空间2.2. HSV、YUV、RGB2.3. 不同颜色空间应用场景 〇、Coding实战内容 OpenCV imread()方法不同的flags差异性获取单色通道【R通道、G通道、B通道】HSV、YUV、RGB 一…

基于学生心理学算法优化的BP神经网络(预测应用) - 附代码

基于学生心理学算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于学生心理学算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.学生心理学优化BP神经网络2.1 BP神经网络参数设置2.2 学生心理学算法应用 4.测试结果&…

证券低延时环境设置并进行性能测试

BIOS设置BIOS参考信息 关闭 logical Process Virtualization Technology 在System Profiles Settings 中System Profile 选择Performance Workload Profile 选择HPC Profile OS中信息参考在/etc/default/grub文件中添加 intel_idle.max_cstate=0 processor.max_cstate=0 idle=p…

使用Easy Chm制作chm文档步骤

前言 软件发布后需要相应的文档说明&#xff0c;CHM是微软新一代的帮助文件格式&#xff0c;利用HTML作源文&#xff0c;把帮助内容以类似数据库的形式编译储存。因为使用方便&#xff0c;形式多样也常被采用作为电子书的格式&#xff1b; 制作类似的chm文档可以使用Easy Chm软…

pygame实现物体运动拖尾尾迹

文章目录 前言主要内容讲解&#xff1a;代码 总结更多宝藏 前言 &#x1f60e;&#x1f973;&#x1f60e;&#x1f920;&#x1f916;&#x1f648;&#x1f4ad;&#x1f373;&#x1f371; 本文我们来讲一下如何使用pygame实现一个拖尾特效。 主要内容 &#x1f99e;&am…

Day42|leetcode 416. 分割等和子集

01背包问题&#xff08;二维&#xff09; 视频讲解&#xff1a;带你学透0-1背包问题&#xff01;| 关于背包问题&#xff0c;你不清楚的地方&#xff0c;这里都讲了&#xff01;| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili 01背包问题&#xff08;一维、滚动数组…

Linux(多进程与多线程)

目录 1、进程与线程概念 1.1 进程 1.2 线程 1.3 进程与线程区别 2、多进程 2.1多进程概念 2.2 进程相关API 2.3 多进程编程 3、多线程 3.1 多线程概念 3.2 多线程相关API 3.3 多线程编程 1、进程与线程概念 1.1 进程 在计算机科学中&#xff0c;进程是正在执行中…

【PyQt】QGraphicsView场景导出为图片

1 需求 需要将用户绘制的场景导出为图片。即 QGraphicsView中的Scene导出为图片。 2 代码 # 提示&#xff1a;此函数应能访问 QGraphicsView 对象。 # 参考&#xff1a;作者的项目中&#xff0c;此函数在某个QMainWindow类中&#xff0c;作为导出按钮的槽函数。import sys …

QGIS学习2-QGIS设置中文界面、导出地图、修改显示投影、自定义投影等

1、设置中文界面 参照官方给的提示&#xff1a; https://qgis.org/en/site/getinvolved/translate.html 2、QGIS功能介绍 QGIS支持功能还是很全面的。 而且提供了很全面的插件库 https://plugins.qgis.org/plugins/ 3、工程文档介绍 可以直接从菜单栏对工程文档进行操作…

SpringBoot在IDEA里实现热部署

使用步骤 1.引入依赖 <!--devtools热部署--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><scope>true</scope><versi…

QChart——折线

Qchart的图形显示依附于QChartView&#xff0c;创建一个QChartView继承类&#xff0c;通过窗口部件的提升进行图表的显示 一、简单认识QLineSeries QLineSeries属于折线类&#xff0c;它继承于QXYSeries类&#xff0c;可以使用QXYSeries类所有方法&#xff0c;对折线进行属性设…

Jmeter(二十八):beanshell的使用

Beanshell 是一种轻量级的 Java 脚本,纯 Java 编写的,能够动态的执行标准 java 语法及一些扩展脚本语法,类似于 javaScript,在工作中可能用的多的就是: Beanshell 取样器:跟Http取样器并列Beanshell前置处理器:一般放在Http请求下,在请求前处理一些数据Beanshell后置处…

Spring Boot进阶(59):【实战教程】使用Spring Boot集成InfluxDB时序数据库,打造高效数据监控系统!

1. 前言&#x1f525; 前几期我们陆续讲解了Mysql、Redis、PostgreSQL、MongoDB等数据库集成及使用案例&#xff0c;接下来&#xff0c;bug菌继续给同学们介绍一种特殊的数据库&#xff0c;到底是什么数据库呢&#xff1f;它就是InfluxDB(时序数据库)&#xff0c;InfluxDB是一款…

Docker vs. Podman: 选择容器技术的智慧之选

嗨&#xff0c;各位亲爱的程序员小伙伴们&#xff01;当我们步入容器技术的世界&#xff0c;往往会在众多选择中迷茫。两个备受瞩目的容器工具&#xff0c;Docker 和 Podman&#xff0c;都在业界掀起了一股风潮。今天&#xff0c;我将带你深入探索&#xff0c;为什么在 Docker …

day 40 | 139. 单词拆分

139. 单词拆分 首先要明确这个是求排列数&#xff0c;所以是先遍历背包&#xff0c;再遍历物品。 func wordBreak(s string, wordDict []string) bool {dp : make([]bool, len(s) 1)dp[0] truefor j : 1; j < len(s); j{for i : 0; i < len(wordDict); i{if j - len…