Hot100之图论

news/2025/2/4 1:46:24 标签: 图论, 深度优先, 算法, 数据结构, leetcode, java

200岛屿数量

题目

思路解析

把访问过的格子插上棋子

思想是先污染再治理,我们有一个inArea()函数,是判断是否出界了

我们先dfs()放各个方向遍历,然后我们再把这个位置标为0

我们岛屿是连着的1,所以我们遍历完后,我们要把遍历完的位置置为0,防止结果重复

代码

class Solution {
    public int numIslands(char[][] grid) {
        
int count=0;

for(int r=0;r<grid.length;r++)
{
    for(int c=0;c<grid[0].length;c++)
    {

        if(grid[r][c]=='1')
        {
            dfs(r,c,grid);
            count++;
        }
        
    }
}
return count;

    }

private void  dfs(int startindex1,int startindex2,char[][] grid)
{
    if(!inArea(grid,startindex1,startindex2))
    return ;

    if(grid[startindex1][startindex2]=='0')
    return ;

    grid[startindex1][startindex2]='0';

         dfs( startindex1- 1, startindex2,grid);
         dfs(startindex1 + 1, startindex2,grid);
         dfs( startindex1, startindex2 - 1,grid);
         dfs( startindex1, startindex2 + 1,grid);


}


boolean inArea(char[][] grid,int row,int cline)
{
    return row>=0&&row<grid.length&&cline>=0&&
    cline<grid[0].length;
}


}

994腐烂的橘子 

题目

思路解析

我们一开始要统计腐烂的橘子的位置和fresh变量统计有几个新鲜的橘子

我们每腐烂一个新鲜橘子fresh就--

要是最后腐烂完还有新鲜橘子就返回-1,表示不能全部腐烂完

解题思路:我们腐烂的橘子放四个方向腐烂

为了防止重复腐烂,我们每遍历一次旧腐烂橘子就结束,然后把新腐烂橘子加入到List<int[]>里面

然后下次从新腐烂橘子开始腐烂

例如我们for循环的遍历的tmp就是我们的旧腐烂橘子,我们开始腐烂,腐烂的同时往q收集我们的新腐烂橘子

方便我们下次4个方向遍历腐烂橘子

for循环的条件:fresh > 0 && !q.isEmpty(),新鲜橘子>0并且还有能继续遍历的腐烂橘子

while (fresh > 0 && !q.isEmpty()) {
            ans++; // 经过一分钟

            List<int[]> tmp = q;

            q = new ArrayList<>();

            for (int[] pos : tmp) { // 已经腐烂的橘子往四个方向腐烂
                for (int[] d : DIRECTIONS) { // 四方向
                    int i = pos[0] + d[0];
                    int j = pos[1] + d[1];
                    if (0 <= i && i < m && 0 <= j && j < n && grid[i][j] == 1) { // 新鲜橘子
                        fresh--;
                        grid[i][j] = 2; // 变成腐烂橘子
                        q.add(new int[]{i, j});
                    }
                }
            }

        }

代码

class Solution {
    //为了判断是否有永远不会腐烂的橘子(如示例 2),我们可以统计初始新鲜橘子的个数 fresh。
    //在 BFS 中,每有一个新鲜橘子被腐烂,就把 fresh 减一,这样最后如果发现 fresh>0,就意味着有橘子永远不会腐烂,返回 −1
    //新鲜橘子腐烂的时候我们要把1变成2
    private static final int[][] DIRECTIONS = {
  
  {-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 四方向

    public int orangesRotting(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int fresh = 0;
        List<int[]> q = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {

                if (grid[i][j] == 1) {
                    fresh++; // 统计新鲜橘子个数
                } 

                else if (grid[i][j] == 2) {
                    q.add(new int[]{i, j}); // 一开始就腐烂的橘子的下标
                }

            }
        }

        int ans = 0;
        while (fresh > 0 && !q.isEmpty()) {
            ans++; // 经过一分钟

            List<int[]> tmp = q;

            q = new ArrayList<>();

            for (int[] pos : tmp) { // 已经腐烂的橘子往四个方向腐烂
                for (int[] d : DIRECTIONS) { // 四方向
                    int i = pos[0] + d[0];
                    int j = pos[1] + d[1];
                    if (0 <= i && i < m && 0 <= j && j < n && grid[i][j] == 1) { // 新鲜橘子
                        fresh--;
                        grid[i][j] = 2; // 变成腐烂橘子
                        q.add(new int[]{i, j});
                    }
                }
            }
            
        }

        return fresh > 0 ? -1 : ans;
    }
}

207课程表

题目

思路解析

判断是否有环

例如【0,1】【1,0】

在学课程0之前要学课程1

在学课程1之前要学课程0

这就是不可能的,用图来说就是有环

访问过不代表找到了环

所以我们要有三种状态

未访问 0

正在访问 1

访问完毕 2

我们最多有numCourses个课程,所以我们遍历numCourses次

for (int i = 0; i < numCourses; i++) {
            if (colors[i] == 0 && dfs(i, g, colors)) {
                return false; // 有环
            }
        }

我们一个节点可能对应多个学习顺序

例如0->1,0->2我们学习1,2之前一个要学习0,所以我们可以顺便把1,2两个节点都dfs完

我们把节点都遍历完后我们都没找到环,就说明我们的该节点x已经访问完毕,我们标记为2

private boolean dfs(int x, List<Integer>[] g, int[] colors) {

        colors[x] = 1; // x 正在访问中

        //开始遍历该节点
        //例如0->1,0->2,我们这个节点是0节点,我们就要遍历1,2
        for (int y : g[x]) {
            if (colors[y] == 1 || colors[y] == 0 && dfs(y, g, colors)) {
                return true; // 找到了环
            }
        }

        colors[x] = 2; // x 完全访问完毕
        return false; // 没有找到环
    }

代码

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {

        List<Integer>[] g = new ArrayList[numCourses];
        Arrays.setAll(g, i -> new ArrayList<>());
        //初始化课程,也就是0->1,0->2,1->3这样子学习课程顺序初始化
        for (int[] p : prerequisites) {
            g[p[1]].add(p[0]);
        }

        //标记染色的数组
        int[] colors = new int[numCourses];
        //因为最多有numCourses个课程,所以我们最多遍历numCourses次
        for (int i = 0; i < numCourses; i++) {
            if (colors[i] == 0 && dfs(i, g, colors)) {
                return false; // 有环
            }
        }

        return true; // 没有环
    }

    private boolean dfs(int x, List<Integer>[] g, int[] colors) {

        colors[x] = 1; // x 正在访问中

        //开始遍历该节点
        //例如0->1,0->2,我们这个节点是-节点,我们就要遍历1,2
        for (int y : g[x]) {
            if (colors[y] == 1 || colors[y] == 0 && dfs(y, g, colors)) {
                return true; // 找到了环
            }
        }

        colors[x] = 2; // x 完全访问完毕
        return false; // 没有找到环
    }
}

208实现前缀树 

题目

思路解析

26叉树结构

首先我们是26个字母,所以我们应该是26叉树

我们定义一个Node结构来实现这个26叉树

然后我们还有个end标志位,标志这个位置是否是一个一个完整的单词

class Node{
    Node[] son=new Node[26];
    boolean end;
}

例如下面

我们的apple是一个完整的单词

我们的app是一个完整的单词

但我们是一个遍历顺序,所以我们这个end标志位是必要的

前缀树的初始化

我们有个root节点,保证所有的字符串都从root开始

   private Node root;

    public Trie() {
        root=new Node();
    }
插入

相当于我们不断构建树

我们Node结构中有一个Node数组 Node[] son=new Node[26]

我们用0,1,2,3表示a,b,c,d......

然后我们移动到遍历的字符的位置

最后单词遍历完毕我们有个end标志位,标志结束

public void insert(String word) {

    Node cur = root; // 从根节点开始

    for (char c : word.toCharArray()) {
        c -= 'a'; // 将字符转换为索引('a' -> 0, 'b' -> 1, ..., 'z' -> 25)
        if (cur.son[c] == null) { // 如果当前字符对应的子节点不存在
            cur.son[c] = new Node(); // 创建新节点
        }
        cur = cur.son[c]; // 移动到子节点
    }

    cur.end = true; // 标记单词的结尾

    }
find找单词

也就是从root节点开始找单词

private int find(String word) {
    Node cur = root; // 从根节点开始
    for (char c : word.toCharArray()) {
        c -= 'a'; // 将字符转换为索引
        if (cur.son[c] == null) { // 如果当前字符对应的子节点不存在
            return 0; // 返回 0,表示未找到
        }
        cur = cur.son[c]; // 移动到子节点
    }
    return cur.end ? 2 : 1; // 如果当前节点是单词结尾,返回 2;否则返回 1
}
search和startsWith

search:如果等于2说明前缀树中有这个单词

find:如果等于1或2说明我们前缀树中有这个单词前缀

public boolean search(String word) {
        return find(word)==2;//检查是否是完整单词
    }

    public boolean startsWith(String prefix) {
        return find(prefix) != 0;
    }

代码

class Node{
    Node[] son=new Node[26];
    boolean end;
}

class Trie {

    private Node root;

    public Trie() {
        root=new Node();
    }
    
    public void insert(String word) {

    Node cur = root; // 从根节点开始

    for (char c : word.toCharArray()) {
        c -= 'a'; // 将字符转换为索引('a' -> 0, 'b' -> 1, ..., 'z' -> 25)
        if (cur.son[c] == null) { // 如果当前字符对应的子节点不存在
            cur.son[c] = new Node(); // 创建新节点
        }
        cur = cur.son[c]; // 移动到子节点
    }

    cur.end = true; // 标记单词的结尾

    }
    
    private int find(String word) {
    Node cur = root; // 从根节点开始
    for (char c : word.toCharArray()) {
        c -= 'a'; // 将字符转换为索引
        if (cur.son[c] == null) { // 如果当前字符对应的子节点不存在
            return 0; // 返回 0,表示未找到
        }
        cur = cur.son[c]; // 移动到子节点
    }
    return cur.end ? 2 : 1; // 如果当前节点是单词结尾,返回 2;否则返回 1
}
    
    public boolean search(String word) {
        return find(word)==2;//检查是否是完整单词
    }

    public boolean startsWith(String prefix) {
        return find(prefix) != 0;
    }


}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

 


http://www.niftyadmin.cn/n/5841159.html

相关文章

Linux环境下的Java项目部署技巧:环境安装

安装 JDK&#xff1a; 第上传 jdk 压缩安装包到服务器 将压缩安装包解压缩&#xff1a; tar -xvf jdk-8uXXX-linux-x64.tar.gz 配置环境变量&#xff1a; 编辑 /etc/profile 文件&#xff0c;在文件末尾添加以下内容&#xff1a; export JAVA_HOME/path/to/jdk //JAVA_HOME…

51单片机 01 LED

一、点亮一个LED 在STC-ISP中单片机型号选择 STC89C52RC/LE52RC&#xff1b;如果没有找到hex文件&#xff08;在objects文件夹下&#xff09;&#xff0c;在keil中options for target-output- 勾选 create hex file。 如果要修改编程 &#xff1a;重新编译-下载/编程-单片机重…

分库分表技术方案选型

一、MyCat 官方网站&#xff0c;技术文档 MyCat是一款由阿里Cobar演变而来的用于支持数据库读写分离、分片的数据库中间件。它基于MySQL协议&#xff0c;实现了MySQL的协议和能力&#xff0c;并作为代理层位于应用和数据库之间&#xff0c;可以隐藏底层数据库的复杂性。 原理…

基于YOLO11的遥感影像山体滑坡检测系统

基于YOLO11的遥感影像山体滑坡检测系统 (价格90) 按照7&#xff1a;2&#xff1a;1随机划分&#xff1a;训练集 6736张 验证集 1924张 测试集 963张 包含 [slide] [山体滑坡] 1种情况 通过PYQT5构建UI界面&#xff0c;包含图片检测&#xff0c;视频检测&…

三. Redis 基本指令(Redis 快速入门-03)

三. Redis 基本指令(Redis 快速入门-03) 文章目录 三. Redis 基本指令(Redis 快速入门-03)1. Redis 基础操作&#xff1a;2. 对 key(键)操作&#xff1a;3. 对 DB(数据库)操作4. 最后&#xff1a; Reids 指定大全(指令文档)&#xff1a; https://www.redis.net.cn/order/ Redis…

使用 EXISTS 解决 SQL 中 IN 查询数量过多的问题

在 SQL 查询中&#xff0c;当我们面对需要在 IN 子句中列举大量数据的场景时&#xff0c;查询的性能往往会受到显著影响。这时候&#xff0c;使用 EXISTS 可以成为一种优化的良方。 问题的来源 假设我们有两个表&#xff0c;orders 和 customers&#xff0c;我们需要查询所有…

为什么就Kafka有分区?

引言 消息队列很多&#xff0c;RocketMQ RabbitMQ ActiveMQ 多数是BrokerQuene的策略&#xff0c;本篇文章详细分析Kafka的分区技术选型的意义和作用 并发处理能力 并行处理消息&#xff1a;分区允许 Kafka 在多个分区上并行处理消息。不同的分区可以分布在不同的 Broker 节…

BitLocker技巧与经验

初级代码游戏的专栏介绍与文章目录-CSDN博客 BitLocker是windows默认的存储加密方案&#xff0c;用好了很安全&#xff0c;用错了完蛋。以下来自我的使用经验。 目录 可以加密移动设备 可以加密操作系统分区 TPM是个坑 一定要用微软账号登录并将密钥保存在账号里 不建议使…