Skip to content

Commit 32851b8

Browse files
authored
Merge pull request MisterBooo#64 from xiaoshuai96/master
solved @xiaoshuai96
2 parents cd3fb6d + cdf6c9e commit 32851b8

File tree

6 files changed

+195
-0
lines changed

6 files changed

+195
-0
lines changed

0011-maxArea/Animation/maxArea.gif

5.08 MB
Loading

0011-maxArea/Animation/maxArea.mp4

1.75 MB
Binary file not shown.

0011-maxArea/Article/0011-maxArea.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
2+
>
3+
> 同步个人博客:https://www.zhangxiaoshuai.fun
4+
5+
**本题选自leetcode的第11题,medium级别,目前通过率:61.3%**
6+
7+
**题目描述:**
8+
9+
```txt
10+
给你n个非负整数a1,a2,...,an,每个数代表坐标中的一个点(i,ai)。在坐标内画n条垂直线,
11+
垂直线i的两个端点分别为(i,ai)和(i,0)。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
12+
说明:你不能倾斜容器,且n的值至少为2。
13+
示例:
14+
输入:[1,8,6,2,5,4,8,3,7]
15+
输出:49
16+
```
17+
18+
我们都应该听说过**木桶原理**,一个木桶可以装入多少水取决于最短的那块板;而这道题也可以与木桶装水的问题对应上。
19+
很容易的可以得到---->**容器可以容纳水的容量=两条垂直线中最短的那条*两条线之间的距离**
20+
现在的情况是,有很多条线,让你计算两两之间能装的最多的水,其实暴力法之间就能解决这个问题,但是它的时间复杂度也达到了**O(n^2)**
21+
22+
ok,那我们先试试用**暴力法**来解 决问题:
23+
24+
### 1.暴力法
25+
26+
直接上代码:
27+
28+
```java
29+
public int maxArea(int[] height) {
30+
int res = 0;
31+
for(int i = 0;i < height.length;i++){
32+
for(int j = i+1;j < height.length;j++){
33+
int temp = Math.min(height[i],height[j]) * (j-i);
34+
res = Math.max(res,temp);
35+
}
36+
}
37+
return res;
38+
}
39+
```
40+
41+
暴力法是可以通过测试的,但是可以看到**程序执行用时**并不理想
42+
43+
```
44+
执行用时 :440 ms, 在所有 Java 提交中击败了17.44% 的用户
45+
内存消耗 :39.9 MB, 在所有 Java 提交中击败了37.86%的用户
46+
```
47+
48+
### 2.双指针
49+
50+
思路:使用两个指针(**resource****last**)分别指向数组的第一个元素和最后一个元素,然后我们计算这两条“线”之间能容纳的水的容量,并更新最大容量(初始值为0);接着我们需要将指向元素值小的那个指针前移一步,然后重复上面的步骤,直到**resource = last**循环截止。
51+
52+
**GIF动画演示:**
53+
54+
![](../Animation/maxArea.gif)
55+
56+
**来看看代码:**
57+
58+
```java
59+
public int maxArea(int[] height) {
60+
int resource = 0;
61+
int last = height.length - 1;
62+
int res = 0;
63+
while (resource < last) {
64+
if (height[resource] >= height[last]) {
65+
res = Math.max(res, (last - resource) * height[last]);
66+
last--;
67+
} else {
68+
res = Math.max(res, (last - resource) * height[resource]);
69+
resource++;
70+
}
71+
}
72+
return res;
73+
}
74+
```
75+
76+
**可以很明显的看到,虽然内存消耗两者是差不多的,但是双指针的速度比暴力解法的速度可是高出好多倍。**
77+
78+
时间复杂度:**O(n)** 空间复杂度:**O(1)**
79+
80+
```
81+
执行用时 :3 ms, 在所有 Java 提交中击败了92.69% 的用户
82+
内存消耗 :40.3 MB, 在所有 Java 提交中击败了7.86%的用户
83+
```
84+
85+
[视频演示](../Animation/maxArea.mp4)
2.52 KB
Loading
Loading
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
2+
>
3+
> 同步个人博客:https://www.zhangxiaoshuai.fun
4+
5+
本题在leetcode中题目序号994,属于medium级别,目前通过率为50.7%
6+
7+
**题目描述:**
8+
9+
```
10+
在给定的网格中,每个单元格可以有以下三个值之一:
11+
值0代表空单元格;
12+
值1代表新鲜橘子;
13+
值2代表腐烂的橘子。
14+
每分钟,任何与腐烂的橘子(在4个正方向上)相邻的新鲜橘子都会腐烂。返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回-1。
15+
16+
示例1:
17+
输入:[[2,1,1],[1,1,0],[0,1,1]]
18+
输出:4
19+
20+
示例2:
21+
输入:[[2,1,1],[0,1,1],[1,0,1]]
22+
输出:-1
23+
解释:左下角的橘子(第2行,第0列)永远不会腐烂,因为腐烂只会发生在4个正向上。
24+
25+
示例3:
26+
输入:[[0,2]]
27+
输出:0
28+
解释:因为0分钟时已经没有新鲜橘子了,所以答案就是0。
29+
30+
提示:
31+
1<=grid.length<=10
32+
1<=grid[0].length<=10
33+
grid[i][j]仅为0、1或2
34+
```
35+
36+
**由题意:只有腐烂的橘子才可以去污染它周围四个方向上存在的新鲜橘子,且它每一分钟只能污染一次,下一次,被它腐蚀的橘子再去腐蚀自己周边的新鲜橘子,每次只有被新腐蚀的橘子才能继续向外腐蚀(因为旧的腐烂的橘子已经被“包围”了)**
37+
38+
这就很像一个人得了传染病,只要他遇见人就会将病传染给那个人,而被传染的又会去感染别的人(不同的是,这里的橘子的位置是固定的,无法移动)
39+
40+
思路是非常简单的,我们通过动态图直观理解下:
41+
42+
![腐烂的橘子gif演示](../Animation/腐烂的橘子01.gif)
43+
44+
既然理清了思路,那么我们来试试代码:
45+
首先,我们需要知道初始状态下的单元格中有多少腐烂的橘子,并且要将它们的位置信息保存下来,我们可以用一个队列(**先入先出**)将(x,y)保存下来;然后我们开始遍历整个队列,每次弹出一个保存的位置信息,将这个位置周围的新鲜橘子全部腐蚀,并且将被腐蚀的橘子的位置信息存入队列中,在下次循环中从它们的位置上再“**向外延伸**”(注意:为了模拟同步,我们需要将每次存入队列中的所有位置都要在下一次全部取出来);直到队列为空,循环结束,这个时候并不能说明整个单元格中已经不存在新鲜的橘子,因为可能存在下面这种情况:
46+
47+
![](../Animation/example01.png)
48+
49+
很明显,标红的区域(新鲜橘子)永远不能被腐蚀,因为它周围唯一的两个单元格是空的。
50+
51+
那么针对这种情况,我们在前面遍历统计腐烂橘子的时候可以顺便统计一下新鲜橘子的数量count,后面我们每腐蚀一个橘子就从count中减去1。最终循环结束的时候,我们只需要判断count是否大于0,若是,返回-1,否则返回轮数res。
52+
53+
------
54+
55+
**代码:**
56+
57+
```java
58+
public static int orangesRotting02(int[][] grid){
59+
int row = grid.length,col = grid[0].length;
60+
Queue<int[]> queue = new ArrayDeque();
61+
int count = 0;//统计新鲜橘子的数量
62+
for (int i = 0; i < row; i++) {
63+
for (int j = 0; j < col; j++) {
64+
if (grid[i][j] == 2) {
65+
queue.add(new int[]{i,j});
66+
}
67+
if (grid[i][j] == 1) {
68+
count++;
69+
}
70+
}
71+
}
72+
int res = 0;
73+
while (count > 0 && !queue.isEmpty()) {
74+
res++;
75+
int size = queue.size();
76+
for (int i = 0; i < size; i++) {
77+
int[] temp = queue.poll();
78+
int r = temp[0],c = temp[1];//(x,y)
79+
//
80+
if (r > 0 && grid[r-1][c] == 1) {
81+
grid[r-1][c] = 2;
82+
count--;
83+
queue.add(new int[]{r-1,c});
84+
}
85+
//
86+
if (r < grid.length-1 && grid[r+1][c] == 1) {
87+
grid[r+1][c] = 2;
88+
count--;
89+
queue.add(new int[]{r+1,c});
90+
}
91+
//
92+
if (c > 0 && grid[r][c-1] == 1) {
93+
grid[r][c-1] = 2;
94+
count--;
95+
queue.add(new int[]{r,c-1});
96+
}
97+
//
98+
if (c < grid[0].length-1 && grid[r][c+1] == 1) {
99+
grid[r][c+1] = 2;
100+
count--;
101+
queue.add(new int[]{r,c+1});
102+
}
103+
}
104+
}
105+
if (count > 0) {
106+
return -1;
107+
}
108+
return res;
109+
}
110+
```

0 commit comments

Comments
 (0)