搜索题目:图像渲染

文章目录

题目

标题和出处

标题:图像渲染

出处:733. 图像渲染

难度

4 级

题目描述

要求

有一个图像以 m × n \texttt{m} \times \texttt{n} m×n 的整数网格 image \texttt{image} image 表示,其中 image[i][j] \texttt{image[i][j]} image[i][j] 表示该图像的像素值。

另外给定三个整数 sr \texttt{sr} sr、 sc \texttt{sc} sc 和 color \texttt{color} color。需要从像素 image[sr][sc] \texttt{image[sr][sc]} image[sr][sc] 开始对图像洪水填充

为了完成洪水填充 ,考虑起始像素点以及与起始像素点在四个方向上连接且与起始像素点的颜色相同的像素点,以及与这些像素点在四个方向上连接且颜色相同的像色淀,以此类推。将上述所有像素点的颜色都改为 color \texttt{color} color。

返回经过洪水填充后的图像。

示例

示例 1:

输入: image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, color = 2 \texttt{image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, color = 2} image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, color = 2

输出: [[2,2,2],[2,2,0],[2,0,1]] \texttt{[[2,2,2],[2,2,0],[2,0,1]]} [[2,2,2],[2,2,0],[2,0,1]]

解析:从图像的正中间, (sr, sc) = (1, 1) \texttt{(sr, sc)} = \texttt{(1, 1)} (sr, sc)=(1, 1)(即红色的像素点)开始,在路径上所有符合条件的像素点的颜色都被更改成新颜色。

注意,右下角的像素点没有更改为 2 \texttt{2} 2,因为它不是在四个方向上与起始像素点相连的像素点。

示例 2:

输入: image = [[0,0,0],[0,0,0]], sr = 0, sc = 0, color = 2 \texttt{image = [[0,0,0],[0,0,0]], sr = 0, sc = 0, color = 2} image = [[0,0,0],[0,0,0]], sr = 0, sc = 0, color = 2

输出: [[2,2,2],[2,2,2]] \texttt{[[2,2,2],[2,2,2]]} [[2,2,2],[2,2,2]]

数据范围

  • m = image.length \texttt{m} = \texttt{image.length} m=image.length
  • n = image[i].length \texttt{n} = \texttt{image[i].length} n=image[i].length
  • 1 ≤ m, n ≤ 50 \texttt{1} \le \texttt{m, n} \le \texttt{50} 1≤m, n≤50
  • 0 ≤ image[i][j], color < 2 16 \texttt{0} \le \texttt{image[i][j], color} < \texttt{2}^\texttt{16} 0≤image[i][j], color<216
  • 0 ≤ sr < m \texttt{0} \le \texttt{sr} < \texttt{m} 0≤sr<m
  • 0 ≤ sc < n \texttt{0} \le \texttt{sc} < \texttt{n} 0≤sc<n

解法一

思路和算法

首先获得 image [ sr ] [ sc ] \textit{image}[\textit{sr}][\textit{sc}] image[sr][sc] 的原始颜色 originalColor \textit{originalColor} originalColor,如果 originalColor = color \textit{originalColor} = \textit{color} originalColor=color,则原始颜色与新颜色相同,不需要改任何像素点的颜色,直接返回 image \textit{image} image。

如果 originalColor ≠ color \textit{originalColor} \ne \textit{color} originalColor=color,则需要从起始像素点 ( sr , sc ) (\textit{sr}, \textit{sc}) (sr,sc) 开始,将所有颜色相同且在四个方向上相连的像素点的颜色改为新颜色 color \textit{color} color。

由于需要改颜色的像素点在图中相连,因此可以使用广度优先搜索实现图像渲染。

首先将 image [ sr ] [ sc ] \textit{image}[\textit{sr}][\textit{sc}] image[sr][sc] 的颜色改为 color \textit{color} color,然后访问与起始像素点连接且颜色为 originalColor \textit{originalColor} originalColor 的所有像素点,将颜色改为 color \textit{color} color。遍历结束之后,返回 image \textit{image} image。

由于只有当 originalColor ≠ color \textit{originalColor} \ne \textit{color} originalColor=color 时才需要执行图像渲染,且每个需要改颜色的像素点在访问之后都改为新颜色,因此可以根据像素点的颜色判断像素点是否访问过,像素点的颜色为 originalColor \textit{originalColor} originalColor 表示未访问,像素点的颜色不为 originalColor \textit{originalColor} originalColor 表示已访问。

代码

java 复制代码
class Solution {
    static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        int originalColor = image[sr][sc];
        if (originalColor == color) {
            return image;
        }
        int m = image.length, n = image[0].length;
        image[sr][sc] = color;
        Queue<int[]> queue = new ArrayDeque<int[]>();
        queue.offer(new int[]{sr, sc});
        while (!queue.isEmpty()) {
            int[] cell = queue.poll();
            int row = cell[0], col = cell[1];
            for (int[] dir : dirs) {
                int newRow = row + dir[0], newCol = col + dir[1];
                if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n && image[newRow][newCol] == originalColor) {
                    image[newRow][newCol] = color;
                    queue.offer(new int[]{newRow, newCol});
                }
            }
        }
        return image;
    }
}

复杂度分析

  • 时间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是图像 image \textit{image} image 的行数和列数。广度优先搜索最多需要访问每个像素点一次。

  • 空间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是图像 image \textit{image} image 的行数和列数。队列需要 O ( m n ) O(mn) O(mn) 的空间。

解法二

思路和算法

如果 image [ sr ] [ sc ] \textit{image}[\textit{sr}][\textit{sc}] image[sr][sc] 的原始颜色 originalColor \textit{originalColor} originalColor 与新颜色 color \textit{color} color 不同,也可以使用深度优先搜索实现图像渲染。

首先将 image [ sr ] [ sc ] \textit{image}[\textit{sr}][\textit{sc}] image[sr][sc] 的颜色改为 color \textit{color} color,然后访问与起始像素点连接且颜色为 originalColor \textit{originalColor} originalColor 的所有像素点,将颜色改为 color \textit{color} color。遍历结束之后,返回 image \textit{image} image。

由于只有当 originalColor ≠ color \textit{originalColor} \ne \textit{color} originalColor=color 时才需要执行图像渲染,且每个需要改颜色的像素点在访问之后都改为新颜色,因此可以根据像素点的颜色判断像素点是否访问过,像素点的颜色为 originalColor \textit{originalColor} originalColor 表示未访问,像素点的颜色不为 originalColor \textit{originalColor} originalColor 表示已访问。

代码

java 复制代码
class Solution {
    static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    int[][] image;
    int m, n;
    int originalColor;
    int color;

    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        originalColor = image[sr][sc];
        if (originalColor == color) {
            return image;
        }
        this.image = image;
        this.m = image.length;
        this.n = image[0].length;
        this.color = color;
        dfs(sr, sc);
        return image;
    }

    public void dfs(int row, int col) {
        image[row][col] = color;
        for (int[] dir : dirs) {
            int newRow = row + dir[0], newCol = col + dir[1];
            if (newRow >= 0 && newRow < m && newCol >= 0 && newCol < n && image[newRow][newCol] == originalColor) {
                dfs(newRow, newCol);
            }
        }
    }
}

复杂度分析

  • 时间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是图像 image \textit{image} image 的行数和列数。深度优先搜索最多需要访问每个像素点一次。

  • 空间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是图像 image \textit{image} image 的行数和列数。递归调用栈需要 O ( m n ) O(mn) O(mn) 的空间。

解法三

预备知识

该解法涉及到并查集。

并查集是一种树型的数据结构,用于处理不相交集合的合并与查询问题。

思路和算法

首先获得 image [ sr ] [ sc ] \textit{image}[\textit{sr}][\textit{sc}] image[sr][sc] 的原始颜色 originalColor \textit{originalColor} originalColor,如果 originalColor = color \textit{originalColor} = \textit{color} originalColor=color,则原始颜色与新颜色相同,不需要改任何像素点的颜色,直接返回 image \textit{image} image。

如果 originalColor ≠ color \textit{originalColor} \ne \textit{color} originalColor=color,则需要执行图像渲染。由于需要改颜色的像素点在图中相连,因此需要改颜色的像素点为与起始像素点在同一个连通分量中的全部像素点,连通性问题可以使用并查集解决。

并查集初始化时,每个像素点分别属于不同的集合,每个集合只包含一个像素点。

初始化之后,遍历每个像素点,如果一个像素点的颜色与其上边或左边的相邻像素点的原色相同,则将两个相邻像素点所在的集合做合并。

完成合并操作之后,获得起始像素点所在的集合,再次遍历所有像素点,对于每个与起始像素点所在的集合相同的像素点,将颜色改为 color \textit{color} color。

完成图像渲染之后,返回 image \textit{image} image。

代码

java 复制代码
class Solution {
    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        int originalColor = image[sr][sc];
        if (originalColor == color) {
            return image;
        }
        int m = image.length, n = image[0].length;
        UnionFind uf = new UnionFind(m * n);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i > 0 && image[i][j] == image[i - 1][j]) {
                    uf.union(i * n + j, (i - 1) * n + j);
                }
                if (j > 0 && image[i][j] == image[i][j - 1]) {
                    uf.union(i * n + j, i * n + j - 1);
                }
            }
        }
        int sRoot = uf.find(sr * n + sc);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (uf.find(i * n + j) == sRoot) {
                    image[i][j] = color;
                }
            }
        }
        return image;
    }
}

class UnionFind {
    private int[] parent;
    private int[] rank;

    public UnionFind(int n) {
        parent = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
        rank = new int[n];
    }

    public void union(int x, int y) {
        int rootx = find(x);
        int rooty = find(y);
        if (rootx != rooty) {
            if (rank[rootx] > rank[rooty]) {
                parent[rooty] = rootx;
            } else if (rank[rootx] < rank[rooty]) {
                parent[rootx] = rooty;
            } else {
                parent[rooty] = rootx;
                rank[rootx]++;
            }
        }
    }

    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }
}

复杂度分析

  • 时间复杂度: O ( m n × α ( m n ) ) O(mn \times \alpha(mn)) O(mn×α(mn)),其中 m m m 和 n n n 分别是图像 image \textit{image} image 的行数和列数, α \alpha α 是反阿克曼函数。并查集的初始化需要 O ( m n ) O(mn) O(mn) 的时间,然后遍历 m n mn mn 个像素点,执行 O ( m n ) O(mn) O(mn) 次合并操作,这里的并查集使用了路径压缩和按秩合并,单次操作的时间复杂度是 O ( α ( m n ) ) O(\alpha(mn)) O(α(mn)),因此并查集初始化之后的操作的时间复杂度是 O ( m n × α ( m n ) ) O(mn \times \alpha(mn)) O(mn×α(mn)),总时间复杂度是 O ( m n + m n × α ( m n ) ) = O ( m n × α ( m n ) ) O(mn + mn \times \alpha(mn)) = O(mn \times \alpha(mn)) O(mn+mn×α(mn))=O(mn×α(mn))。

  • 空间复杂度: O ( m n ) O(mn) O(mn),其中 m m m 和 n n n 分别是图像 image \textit{image} image 的行数和列数。并查集需要 O ( m n ) O(mn) O(mn) 的空间。

相关推荐
伟大的车尔尼2 天前
搜索题目:甲板上的战舰
并查集·深度优先搜索·广度优先搜索
伟大的车尔尼8 天前
广度优先搜索和深度优先搜索的概念
数据结构·算法·并查集·深度优先搜索·广度优先搜索
像素猎人1 个月前
以数据结构之——树来体会深度优先搜索【dfs】和广度优先搜索【bfs】的妙用:学比特算法课的自用笔记
数据结构·c++·学习·dfs·bfs·深度优先搜索
I_LPL1 个月前
day52 代码随想录算法训练营 图论专题5
java·算法·图论·并查集
adam_life1 个月前
并查集(非连通性问题)——# P2391 白雪皑皑
并查集·非连通性问题·路径压缩——跳过已处理问题·遍历顺序技巧
Darkwanderor2 个月前
数据结构 - 并查集的应用
数据结构·c++·并查集
老鼠只爱大米2 个月前
LeetCode经典算法面试题 #236:二叉树的最近公共祖先(RMQ转化、Tarjan离线算法等五种实现方案详细解析)
算法·leetcode·二叉树·lca·并查集·最近公共祖先·rmq
老鼠只爱大米2 个月前
LeetCode经典算法面试题 #199:二叉树的右视图(BFS双队列法、DFS递归法等多种实现方案详细解析)
算法·leetcode·二叉树·dfs·bfs·深度优先搜索·右视图
Queenie_Charlie2 个月前
八皇后问题
c++·深度优先搜索