「这是我参与2022首次更文挑战的第33天,活动详情查看:2022首次更文挑战」
前言
笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。
系列文章收录《算法》专栏中。
问题描述
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
提示:
- n == height.length
- 2 <= n <= 105
- 0 <= height[i] <= 104
剖析
我们先确定下怎么计算容器容水量,设现在height[i]和height[j]两条线和x轴组成的容器可以存放更多的水,p为容水量,那么p=|i-j|*min(height[i],height[j])。我们观察一下,在|i-j|能够保证最大的情况下,min(height[i],height[j])我们不断挪动把“较小值”变大尝试找到p最大的。如果在“把较小值变大”的过程中只要遇到比“较小值”大1的挪动都是有效的至少p没有变小,所以我们按照这个思路去进行挪动,下面描述下算法步骤:
- 左右两个指针指向最小下标i和最大小标j,计算p。
- 如果height[i]小左指针就往右移动,如果height[j]小右指针就往左移动,移动一下计算一下面积,返回最大面积。
时间复杂度为O(n),空间复杂度为O(1)。
注意:此算法实现步骤比较简单,注意++i和i++的区别,尽量使用前置,后面会出一篇字节码相关文章从字节码的角度来进行解释。
代码
public class MaxArea {
/**
* 我们先确定下怎么计算容器容水量,设现在height[i]和height[j]两条线和x轴组成的容器可以存放更多的水,p为容水量,那么p=|i-j|*min(height[i],height[j])。我们观察一下,在|i-j|能够保证最大的情况下,min(height[i],height[j])我们不断挪动把“较小值”变大尝试找到p最大的。如果在“把较小值变大”的过程中只要遇到比“较小值”大1的挪动都是有效的至少p没有变小,所以我们按照这个思路去进行挪动,下面描述下算法步骤:
* 1. 左右两个指针指向最小下标i和最大小标j,计算p。
* 2. 如果height[i]小左指针就往右移动,如果height[j]小右指针就往左移动,移动一下计算一下面积,返回最大面积。
* <p>
* 时间复杂度为O(n),空间复杂度为O(1)。
*
* @param height
* @return
*/
public static int maxArea(int[] height) {
int p = 0;
int i = 0;
int j = height.length - 1;
while (i < j) {
int pTemp = (j - i) * Math.min(height[i], height[j]);
if (height[i] <= height[j]) {
++i;
} else {
--j;
}
p = Math.max(pTemp, p);
}
return p;
}
public static void main(String[] args) {
System.out.println(maxArea(new int[]{1, 8, 6, 2, 5, 4, 8, 3, 7}));
}
}