问题1(LeetCode 42):源码
给定一个非负数的数组,代表一个容器。例如数组[0,1,0,2,1,0,1,3,2,1,2,1],就是以下图形中黑色的部分。如果用这个容器接水的话,请问可以接多少水?还以这个数组为例,可以接6格水,就是以下图形中蓝色的部分
解题思路:
简化题意:如果能求得当前位置格子上的水量,那么总水量就是每个位置水量之和。当前格子上所能存储的水量 = 当前格子左边最大值与右边最大值之间的较小值 - 当前格子高度
[Math.max(0, Math.min(leftMax, rightMax[index - 1]) - height[index])]
- 时间复杂度O(N), 空间复杂度O(N)的算法
/***
* 时间复杂度O(N), 空间复杂度O(N)的算法。
* 1.先求出当前格子左边的最大值与右边最大值,对于右边最大值用数组rightMax 数组来辅助存储,从右往左遍历一下原数组即可得到rightMax
* 2. 对于左边的最大值在遍历原数组的同时用一个全局变量记录下来就行,此时时间复杂度为O(n) 空间复杂度O(n)
*/
public int getWeight(int[] height) {
if (height == null || height.length <= 3) {
return 0;
}
int n = height.length - 2;
int[] rightMax = new int[n];
rightMax[n - 1] = height[n + 1];
int totalValue = 0;
for (int index = n - 2; index >= 0; index--) {
rightMax[index] = Math.max(height[index + 2], rightMax[index + 1]);
}
int leftMax = height[0];
for (int index = 1; index <= n; index++) {
totalValue += Math.max(0, Math.min(leftMax, rightMax[index - 1]) - height[index]);
leftMax = Math.max(leftMax, height[index]); //下一次遍历的时候最大leftMax
}
return totalValue;
}
- 时间复杂度O(N), 空间复杂度为O(1) 的算法
/****
* 时间复杂度O(N), 空间复杂度为O(1) 的算法。
* 1. 双指针left和right 遍历 数组, 求取每个位置上可容纳的水量。分别指向数组的第二个元素和倒数第二个元素,可知第一个元素和最后一个元素的储水量都是零
* 2. leftMax 和rightMax 记录left 指针 左边的最大值。 rightMax 记录right 指针右边的最大值
* 3. totalWater 记录 总的储水量
* 4. 若leftMax < rightMax则结算左指针height[left],leftMax > height[left]时,totalWater += leftMax - height[left++],反之更新左边最大值leftMax = height[left++],左指针向右移一位;
* 5. 反之,结算右边的当前元素,过程与左边类似
* @param height
* @return
*/
public int getWeight1(int[] height) {
if (height == null || height.length < 3) {
return 0;
}
int left = 1;
int right = height.length - 2;
int totalWater = 0;
int leftMax = height[0];
int rightMax = height[height.length - 1];
while (left <= right) {
if (leftMax < rightMax) {
if (leftMax > height[left]) {
totalWater += leftMax - height[left++];
} else {
leftMax = height[left++];
}
} else {
if (rightMax > height[right]) {
totalWater += rightMax - height[right--];
} else {
rightMax = height[right--];
}
}
}
return totalWater;
}
问题2(LeetCode 11):源码
给定一个非负数的数组,数组中的每个值代表一个柱子的高度,柱子的宽度是1。两个柱子之间可以围成一个面积,规定:面积=两根柱子的最小值*两根柱子之间的距离。比如数组[3,4,2,5]。3和4之间围成的面积为0,因为两个柱子是相邻的,中间没有距离。3和2之间围成的面积为2,因为两个柱子的距离为1,且2是最短的柱子,所以面积=12。
3和5之间围成的面积为6,因为两个柱子的距离为2,且3是最短的柱子,所以面积=32。求在一个数组中,哪两个柱子围成的面积最大,并返回值
/****
* 1. 两个指针left 和right, 分别向中间移动。area = Math.min(arr[left], arr[right]) * (right - left)
* 2. 如果 arr[left] > arr[right], 那么 right 向左移动, 否则 left 向右移动。
* (为什么需要较小的移动? 因为1 中的面积为arr[left], arr[right]较小者乘以宽度,此时 较小者为瓶颈, 移动最小者:
* 举例:arr[left] = 6, arr[right] = 7, right-left=10, 此时面积为60
* 如果 此时移动right, 会出现两种情况
* 1. arr[right-1] > 6, 此时面积为 6*9 < 60
* 2. arr[right - 1] < 6, 此时面积为 arr[right-1] * 9 < 60
* <p>
* 所以移动较小者 才有可能获取到更大的面积。)
*/
public int getMaxArea(int[] height) {
if (height == null || height.length < 2) {
return 0;
}
int left = 0;
int right = height.length - 1;
int max = Integer.MIN_VALUE;
while (left <= right) {
// max = Math.max(max, (right-left)*Math.min(height[left], height[right])); 耗时 9ms, 用下面的耗时7ms
if (height[left] < height[right]) {
int cur = height[left] * (right - left);
max = max > cur ? max : cur;
left++;
} else {
int cur = height[right] * (right - left);
max = max > cur ? max : cur;
right--;
}
}
return max;
}