当青训营遇上码上掘金——主题4:攒青豆
前言
由于本人是编程小白,只在学校学习了一学期的c语言和简单的算法,所以尝试用c语言解题。
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度
在这种情况下,可以接 17 个单位的青豆。
思路
法一
在数组中任意选取一个位置i,求出i左边的最大值,i右边的最大值。i位置能装的豆子取决于2个最大值较小的一个
使用两个数组left和right,left[i]为i位置左边的最大值,将left[i-1]与height[i]比较,取最大值
二者的差值为可以装的豆子
设数组长度为heightLen,从i=1开始遍历到i=heightLen-2,每个位置的豆子总和加在一起,得到总和
注:经过测试,发现以下问题:
①该算法设计不严谨,输出数值与实际值不符,考虑新方法
②由于c语言的局限性,难以实现数组长度可变,会出现数组下标越界访问的问题,考虑用c++的vector类可变数组进行后续改进
法二
从数组两边的端点向中间遍历,记录左边的最大值leftMax和右边的最大值rightMax,当指针相遇时,停止遍历
遇到比最大值小的就+(最大值-当前值)的豆子,遇到比最大值大的就更新最大值,直到两个指针相遇
以left指针为例:
①如果height[left]比leftMax小,直接在总数上+(最大值-当前值)的豆子
②反之不能,更新leftMax的值为height[left]
因为最后一个最大值可能会多加豆,所以每次更新最大值要记录最大值的位置,计算方法同上,将两个最大值比较,按照刚才的方法重算一遍,纠正结果。
代码(算法部分)
法一
#include <stdio.h>
int trap(int *height,int heightSize)
{
if(heightSize<=2){
return 0;
}
int left[heightSize], right[heightSize];
int i,sum=0,min=0;
left[0]=height[0],right[0]=height[0];
for(i=1;i<heightSize;i++){
if(height[i]>left[i-1]){
left[i]=height[i];
}else{
left[i]=left[i-1];
}
}
for(i=heightSize-2;i>=0;i--){
if(height[i]>right[i+1]){
right[i]=height[i];
}else{
right[i]=right[i+1];
}
}
for(i=1;i<heightSize-1;i++){
if(left[i]>right[i]){
min=right[i];
}else{
min=left[i];
}
sum+=min-height[i];
}
return sum;
}
法二
#include <stdio.h>
int trap(int* height, int heightLen)
{
if (heightLen <= 2) {
return 0;
}
int left = 0, right = heightLen - 1;
int leftMax = height[0], rightMax = height[heightLen - 1];
int sum = 0;
while (left <= right) {
if (leftMax < rightMax) {
if (height[left] < leftMax) {
sum += leftMax - height[left];
}
else {
leftMax = height[left];
}
left++;
}
else {
if (height[right] < rightMax) {
sum += rightMax - height[right];
}
else {
rightMax = height[right];
}
right--;
}
}
return sum;
}