当青训营遇上码上掘金
当青训营遇上码上掘金-攒青豆的四种解法
题目描述
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解法一:
暴力求解,时间复杂度,空间复杂度
解题思路: 统计除去首位外每个柱子上方所接的水的面积,对于每个柱子而言,其所接水的面积取决于左右两边的最大柱子高度中较小的一个。
算法描述:
- 依次遍历数组
height[1:len(height)-1]中的每个元素; - 当遍历到下标为
i的元素时,执行步骤3、4、5; - 遍历
height[:i],找到i对应的最大左元素left_max; - 遍历
height[i+1:],找到i对应的最大右元素right_max; - 下标为
i的柱子所接的水为max(min(left_max, right_max)-height[i], 0); - 将全部结果求和得到最终输出;
解法二:
使用动态规划的思想优化解法一的时间复杂度,时间复杂度,空间复杂度
解题思路: 通过一次遍历,将每个元素对应的左大元素和右最大元素记录在数组里。
算法描述:
- 分别用
left_max_ans和right_max_ans来记录左大元素和右最大元素; - 遍历数组
height[1:],更新left_max_ans[i] = max(left_max_ans[i-1],height[i-1]); - 遍历数组
height[:len(height)-1],更新right_max_ans[i] = max(right_max_ans[i+1],height[i+1]); - 遍历数组
height[1:len(height)-1],求每个柱子所承接的水的面积arce = max(min(left_max_ans[i], right_max_ans[i])-height[i], 0); - 求和;
解法三:
使用双指针进一步优化空间复杂度,此题最优解,时间复杂度,空间复杂度
解题思路: 分别用一个变量来记录每个元素对应的左最大元素和右最大元素,用两个指针left和right指向height的两端,left_max和right_max分别表示左最大元素和右最大元素,当height[left-1]<height[right+1]的时候,左最大元素不可能比右最大元素大,因此较小的一方在左边,此时向左扫描left++,否则向右扫描right--。
算法描述:
- 设置初值
left, right, left_max, right_max := 1, len(height)-1, 0, 0 - 如果左右指针没重合
left<=right,向下执行,否则步骤6; - 如果
height[left-1]<height[right+1],执行步骤4,否则执行步骤5; - 更新
left_max = max(left_max,height[left-1])计算left对应柱子的面积max(left_max-height[left], 0),移动左指针left++,转至步骤2; - 更新
right_max = max(right_max,height[right+1])计算right对应柱子的面积max(right_max-height[right], 0),移动右指针right--,转至步骤2; - 求和;
解法四:
使用单调栈,时间复杂度,空间复杂度
解题思路: 用一个单调递减栈(栈内存储数组下标,下标对应的柱子高度单调递减)来记录i对应柱子左边柱子高度的排列情况,当i比栈顶元素对应的柱子高度低时直接入栈,否则出栈,计算出栈后栈顶元素对应的柱子到i承接的水的面积;
算法描述:
- 初始化栈
stack; - 遍历
height[0:]; - 如果栈非空或栈顶元素对应的柱子高度小于
height[i],执行步骤4,否则执行步骤5; - 出栈,记录栈顶元素对应柱子的高度
enum,计算出栈后的栈顶元素对应的柱子到i对应柱子之间所接的水的面积arce := (i-height[stack.top]-1)*;(min(height[stack.top], height[i])-enum),转至步骤3; - 将
i入栈;
PS: 虽然嵌套了两层循环,单每个元素至多访问两遍(出栈、入栈),因此总的时间复杂度为O(n).
附代码
[jcode](https://code.juejin.cn/pen/7198485637895290917)