盛最多水的容器
题目
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
来源:力扣(LeetCode)
题目序号:11
链接:https://leetcode.cn/problems/container-with-most-water
初始思路(堆空间存储装水的所有情况)
第一想法
看到题目,我的第一想法就是遍历,
事实上也肯定是要遍历的,
但遍历什么,遍历过程中要实现什么需要明确。
上面的遍历啥不清楚的话,可以先写一个框架,
然后往后看,
首先看向第一个元素,
如何装水&装水数
因为是要装水的,
根据“木桶效应”,最低的板子决定了装水量,
所以需要判断第一个元素参与了的“木桶”可以装多少水,然后再进行下一步比较,
所以,遍历啥已经很清楚了,
以当前的第一个元素为研究对象的话,就需要判断他和其他元素的大小,
毕竟,对于当前的第一块木板,只有找到一块比它长或者一样长的木板,才能装水,
对于有序数组的话,直接选择排序往后就OK,
很可惜,这里是无序的,所以需要考虑的事情就有点多,
需要考虑的有两点,
一是遍历的顺序,这个其实对于我们来说啥都不是,因为我们习惯性的向右
二是木桶的形状,是左右一样高?还是左高右低?或者说左低右高,
第二点说的明白点,就是如果现在使用选择排序,在遍历取得装水数的时候让第二个for里面的j初始值为i+1,就会出问题:
看见没,很明显的,装水数咋说也不可能这么短吧。
所以说,若想将三种木桶全部考虑到,这个j的初始值必须要为0:
对于上图出现的装水数,题目中已经给了计算方法了:
对于有装水机会的,
找出比他大的数,然后比对下标,算出差值,
然后用这个差值乘以当前元素,就得到了装水数
处理所得的装水数
解决了各种装水的问题,然后就是需要对这些数据进行处理了,
很明显,这些装水的数都是int类型的数,
我们很容易想到可以设一个int类型的数组来装他们,
但是还是会遇到一个问题,那就是数组的长度问题,
这里可以尽量给大一点,
若是实在纠结给多少长度的话,可以试试将这些数据存在堆空间里面,这样一来,这就是一个很实用的变长数组了,
将遍历过程中每个元素得到的装水数,单独存在一个堆空间数组里面
然后在那个新构成的堆空间数组里面寻找最大值,将其返回就行了
初始代码
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
int WATER(int *p, int len)
{
// 遍历
// 判断每一个元素是不是最大的,不是最大就有装水的机会
// 对于有装水机会的,找出比他大的数,然后比对下标,算出差值,然后用这个差值乘以当前元素
// 将遍历过程中每个元素得到的装水数单独存在一个堆空间数组里面
// 然后在那个新构成的数组里面寻找最大值,并返回
int x = 0, y = 0;
// 将所有装水的情况写入堆空间
int *q = (int *)malloc(sizeof(int)*len*len);
bzero(q, sizeof(int)*len*len);
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len; j++)
{
if (p[i]<=p[j])
{
++y; // 记录堆空间中元素的个数
int a = j-i;
int b = abs(a*p[i]);
q[x++] = b;
}
}
}
// 将堆空间的数据从大到小排列,如此一来,只需要返回第一个值就行了
int a = 0;
for (int i = 0; i < y; i++)
{
for (int j = i+1; j < y; j++)
{
if (q[i]<q[j])
{
a = q[i];
q[i] = q[j];
q[j] = a;
}
}
}
return q[0];
}
int main(int argc, char const *argv[])
{
int height[] = {1, 8, 6, 2, 5, 4, 8, 3, 7};
int *p = height;
int len = sizeof(height)/4; // 求出元素的个数
int a = WATER(p, len);
printf("water:\t%d\n", a);
return 0;
}
自我修改
以上的内容都是我自己写的,
可能是由于太低效,在力扣上提交失败,
可以看到,当实验的结果有很多,我存在堆空间中的数据就太大了,有len*len个,
可能正是因为这个,拖慢了我的程序运行速度,甚至出错,
但是程序本身是没有问题的,
用堆空间的这个思路遇到这种情况,进而导致这样的结果,应该是必然的事情
所以,这里在堆空间存所有情况思路之上,优化修改代码的空间,真的不大
堆空间都不行,数组就别想了
力扣上的参考解答
看到了一个很厉害的解答,可以说很完美了,
没用堆空间,而是不断更新最大装水数,
而且运用了头尾双指针,很大程度上减少了无必要的循环次数
关于这个头尾双指针,就是从头尾分别向对侧遍历,
在每次循环中,头尾指针中的某一个满足条件就会移动,
所以,当破开循环条件后,所有的情况其实已经考虑到了,
所以,每遭遇一种情况,就计算出装水数,并与上一次的进行比较,
保留最大的,一直到所有情况比较完,
这样到最后,保留的一定是最大的容量
这是大佬本人的解释:
int maxArea(int* height, int heightSize){
int start = 0;
int end = heightSize - 1;
int max;
int tmp;
max = (end - start) * (height[start] > height[end] ? height[end] : height[start]);
while (end > start) {
tmp = (end - start) * (height[start] > height[end] ? height[end] : height[start]);
max = tmp > max ? tmp : max;
if (height[start] < height[end]) {
start++;
} else if (height[start] > height[end]){
end--;
} else {
start++;
end--;
}
}
return max;
}
将这个函数代替我的函数用到刚刚把我堆空间炸掉的地方: