733. 图像渲染
有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。
给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。
为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。
最后返回经过上色渲染后的图像。
示例 1:
输入:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
输出: [[2,2,2],[2,2,0],[2,0,1]]
解析:
在图像的正中间,(坐标(sr,sc)=(1,1)),
在路径上所有符合条件的像素点的颜色都被更改成2。
注意,右下角的像素没有更改为2,
因为它不是在上下左右四个方向上与初始点相连的像素点。
过程解析:
class Solution {
//采用深度优先搜索(DFS)
//上下左右四个方向
int[] hx={1,0,0,-1};
int[] hy={0,1,-1,0};
public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
int color1=image[sr][sc];
if(color1!=newColor){
DFS(image,sr,sc,color1,newColor);
}
return image;
}
public void DFS(int[][] image,int x,int y,int color,int newColor){
if(image[x][y]==color){
image[x][y]=newColor;
for(int i=0;i<4;i++){
int px=x+hx[i];
int py=y+hy[i];
if(px>=0&&px<image.length&&py>=0&&py<image[0].length){
DFS(image,px,py,color,newColor);
}
}
}
}
}
结果:
执行用时:1 ms, 在所有 Java 提交中击败了64.23%的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了75.75%的用户
通过测试用例:277 / 277
695. 岛屿的最大面积
给你一个大小为 m x n 的二进制矩阵 grid 。
岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
岛屿的面积是岛上值为 1 的单元格的数目。
计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
示例 1:
输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。
示例 2:
输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0
解析过程:
class Solution {
//采用DFS
int[] hx={1,0,0,-1};
int[] hy={0,1,-1,0};
public int maxAreaOfIsland(int[][] grid) {
int num=0;
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
//计算岛屿面积,然后取最大值
num=Math.max(num,DFS(grid,i,j));
}
}
return num;
}
public int DFS(int[][] grid,int x,int y){
//若岛上值为0,则返回0
if(x<0||x==grid.length||y<0||y==grid[0].length||grid[x][y]!=1){
return 0;
}
int num=1;//记录岛屿的面积
grid[x][y]=0; //访问过的岛屿值变为0,以防再次遍历
for(int i=0;i<4;i++){
//上下左右四个位置,从 4 个方向搜索与之相连的每一个土地,以及与这些土地相连的土地,最后搜索到的土地总数为该岛屿的面积
int px=x+hx[i];
int py=y+hy[i];
if(px>=0&&px<grid.length&&py>=0&&py<grid[0].length){
num+=DFS(grid,px,py);
}
}
return num;
}
}
结果:
执行用时:3 ms, 在所有 Java 提交中击败了50.66%的用户
内存消耗:38.9 MB, 在所有 Java 提交中击败了59.98%的用户
通过测试用例:728 / 728
617. 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/
4 5
/ \ \
5 4 7
注意: 合并必须从两个树的根节点开始。
过程解析:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
//DFS
//若该位置只存在一个节点,则该位置的节点就是这个节点
if(root1==null){
return root2;
}
if(root2==null){
return root1;
}
TreeNode node=new TreeNode(root1.val+root2.val);
//递归遍历
node.left=mergeTrees(root1.left,root2.left);
node.right=mergeTrees(root1.right,root2.right);
return node;
}
}
结果:
执行用时:1 ms, 在所有 Java 提交中击败了18.75%的用户
内存消耗:38.9 MB, 在所有 Java 提交中击败了5.29%的用户
通过测试用例:182 / 182
116. 填充每个节点的下一个右侧节点指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例:
输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,’#’ 标志着每一层的结束。
解析过程:
采用层次遍历更方便理解,层次遍历与广度优先搜索的不同地方在于BFS每次取出一个节点扩展,层次遍历采用队列的形式,每次取出同一层的节点。
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
//层次遍历,每次遍历同一层
if(root==null){
return root;
}
//初始化队列
Queue<Node> queue=new LinkedList<Node>();
//将根节点(第一层节点)加入队列中
queue.add(root);
//若层数不为空
while(!queue.isEmpty()){
//记录当前队列大小
int len=queue.size();
for(int i=0;i<len;i++){
//取出节点(先进先出)
//remove() 和 poll() 方法都是从队列中删除第一个元素。remove() 的行为与 Collection 接口的版本相似, 但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。
//poll():检索并删除此队列的头,如果此队列为空,则返回 null 。
Node node=queue.poll();
//peek():查看此队列顶部的对象,而不从队列中删除它。
//element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null。
if(i<len-1){
node.next=queue.peek();
}
//将下一层节点加入队列
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
}
return root; //返回根节点
}
}
结果:
执行用时:3 ms, 在所有 Java 提交中击败了36.16%的用户
内存消耗:38.8 MB, 在所有 Java 提交中击败了20.83%的用户
通过测试用例:58 / 58
542. 01 矩阵
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
示例 1:
输入:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]
示例 2:
输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]
过程解析:
将每个源点 0入队,然后从各个 0 同时开始一圈一圈的向 1 扩散(每个 1都是被离它最近的 0 扩散到的 ),还要注意是否被访问过的问题,因此,将1的位置设为MAX_VALUE,表明该位置还未被访问过
复杂度分析:
时间复杂度:由于每个点都要入队出队一次,所以时间复杂度:O(n * m);
空间复杂度:最差情况下矩阵中元素都为0,此时需要把m*n个0都入队,因此空间复杂度是 O(n * m)
class Solution {
public int[][] updateMatrix(int[][] mat) {
//采用BFS,先将所有的0入队,并将1的位置设为MAX_VALUE,表明该位置还未被访问过
//队列初始化
Queue<int[]> queue=new LinkedList<>();
int m=mat.length;
int n=mat[0].length;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(mat[i][j]==0){ //将所有的0入队
queue.add(new int[] {i,j});
}else {
mat[i][j]=Integer.MAX_VALUE;
}
}
}
//上下左右四个方向
int[] hx={-1,1,0,0};
int[] hy={0,0,-1,1};
//访问队列中的每个元素
//当队列不为空,进行后面操作
while(!queue.isEmpty()){
int[] element=queue.poll();
int x=element[0];
int y=element[1];
for(int i=0;i<4;i++){
int curx=x+hx[i];
int cury=y+hy[i];
if(curx>=0&&curx<m&&cury>=0&&cury<n&&mat[curx][cury]==Integer.MAX_VALUE){
//如果四邻域的点是MAX_VALUE,则这个点是还未被访问过的 1,所以这个点到 0 的距离就更新成 mat[x][y] + 1
mat[curx][cury]=mat[x][y]+1;
//将该点加入队列中
queue.add(new int[] {curx,cury});
}
}
}
return mat;
}
}
结果:
执行用时:12 ms, 在所有 Java 提交中击败了72.84%的用户
内存消耗:40.2 MB, 在所有 Java 提交中击败了93.35%的用户
通过测试用例:50 / 50
994. 腐烂的橘子
在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
示例 1:
输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
示例 3:
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
过程解析:
class Solution {
public int orangesRotting(int[][] grid) {
//BFS,先将腐烂的橘子加入队列当中,将未腐烂的橘子位置设置为-1,表示未被访问过
Queue<int[]> queue=new LinkedList<>();
int minute=0;//记录时间
int num=0;//记录新鲜橘子的个数,如果最终新鲜橘子的个数>0,说明存在无法被污染的橘子
int m=grid.length;
int n=grid[0].length;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==2){
queue.add(new int[] {i,j});
}else if(grid[i][j]==1){
num++;
}
}
}
//当队列不为空
while(num>0&&!queue.isEmpty()){
minute++;
int len=queue.size();
for(int i=0;i<len;i++){
int[] element=queue.poll();
int x=element[0];
int y=element[1];
//此处若是用for循环,则会超出时间限制
//向上
if(x-1>=0&&grid[x-1][y]==1){
grid[x-1][y]=2;
num--;
queue.add(new int[] {x-1,y});
}
//向下
if(x+1<m&&grid[x+1][y]==1){
grid[x+1][y]=2;
num--;
queue.add(new int[] {x+1,y});
}
//向左
if(y-1>=0&&grid[x][y-1]==1){
grid[x][y-1]=2;
num--;
queue.add(new int[] {x,y-1});
}
//向右
if(y+1<n&&grid[x][y+1]==1){
grid[x][y+1]=2;
num--;
queue.add(new int[] {x,y+1});
}
}
}
if(num>0){
return -1;
}else {
return minute;
}
}
}
结果:
执行用时:2 ms, 在所有 Java 提交中击败了73.81%的用户
内存消耗:37.7 MB, 在所有 Java 提交中击败了74.75%的用户
通过测试用例:306 / 306