LeetCode 热题 HOT 100 第7天: “盛最多水的容器”

继续刷LeetCode 热题 HOT 100 的题目,并且在博客更新我的solutions。在csdn博客中我会尽量用文字解释清楚,相关Java代码大家可以前往我的个人博客jinhuaiyu.com中查看。
做题不是为了做这道题,而是为了提取思想解决其他问题。今天的题目用到的思想和前几天都不一样,因此有必要深刻理解加深记忆。
题目:盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 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
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
solution: 缩减搜索空间(双指针)
作为一道中级难度算法题,看似简单的肯定不是用暴力破解,实际上,分治法、动态规划在这里也用不上或者不是优解。双指针可能大家很容易就想到了,但是这道题需要被提炼的精髓在于缩减搜索空间
显而易见,如果讨论每一种可能,复杂度是O(n²),外面要思考有没有一些搜索空间是没有必要被讨论的,也就是在搜索过程中可以被直接跳过。官方给出的结题讲解不够清晰,虽然大家也能理解,但是没有直观体现出缩减搜索空间的魅力。首先,我还是要先简单讲一下解题方法,最后给出解空间被缩减的直观图解。
面积(装水容积)取决于左右两条边的长度,准确地说,取决于其中的短边。我们的双指针从最左右两侧开始移动来讨论,同时用一个变量max记住每个过程中最大的面积。刚开始时,假设左右两侧分别为a和b(a>b,哪个大不影响讨论)长,如果以这两天边为容器左右两边,则面积为b乘以中间的距离d,设为s。如果左侧(长的一侧)往内移动,由于d在变短,而不管此时移到的边有多高,算面积时,高都不能超过b(面积取决于短边)。因此我们可以发现,以最右侧为一条边,其他的情况都不必再讨论了(反正不可能超过s)。
于是搜索空间被缩减了,这些不讨论的相当于把最右侧短边b去除,右指针往内移动一位,讨论此时左右指针内的子情况,当然,如果被除去的b边真的是最终结果的其中一边,那么一定是和最右侧a边构成最大面积,这个结果会在变量max中被更新,不会丢失。
剩下的子情况同理处理,每次更新max后除去当前左右指针中较短的那一条边,便可一次性省去一批情况不用讨论。
显然,最后的时间复杂度为左右指针每轮移动一位直到相遇的n次循环。下面给出搜索空间被缩减的直观图解:
假设有8条边,左右指针为i、j,由于i、j的约束条件的限制,搜索空间是白色的倒三角部分。可以看到,搜索空间的大小是 O(n²)数量级的。
在这里插入图片描述
要想得到 O(n)的解法,我们就需要能够一次排除多个单元格。假设左边的 0号边较短。根据刚才的推理,0号边目前的水面高度已经到了上限。由于 7号边已经是离0 号最远的了,如果换其他的边和 0号边配对,水的宽度只会更小,高度也不会增加,容纳水的面积只会更小。也就是说,0号边和 6, 5, …, 1号边的配对都可以排除掉了。记录了 (0,7) 这组边的结果之后,就可以排除 0号柱子了。这相当于 i=0 的情况全部被排除。对应于双指针解法的代码,就是 i++;对应于搜索空间,就是削减了一行的搜索空间,如下图所示。
在这里插入图片描述
排除掉了搜索空间中的一行之后,我们再看剩余的搜索空间,仍然是倒三角形状。我们检查右上方的单元格 (1, 7),即考虑 1 号边和 7号边,计算它们之间容纳水的面积。然后,比较两条边。
假设此时 7 号边较短。同理,7 号边已经是离1 号最远的了,如果换其他的边和 1号配对,水的宽度变小,高度也不会增加,容纳水的面积只会更小。也就是说,7号边和 2,3,…,6 号边的配对都可以排除掉了。记录了(1,7) 这组边的结果之后,就可以排除 7 号了。这相当于 j=7 的情况全部被排除。对应于双指针解法的代码,就是 j–;对应于搜索空间,就是削减了一列的搜索空间,如下图所示。在这里插入图片描述
可以看到,无论 i 和 j哪条边更长,我们都可以排除掉一行或者一列的搜索空间。经过 n步以后,就能排除所有的搜索空间,检查完所有的可能性。
最后,说明一下如果当前比较的两条边一样长,那么随便排除一条即可,因为如果是以这两条边中一条为最终结果,那最终结果的两条边一定是这两条一样长的边,这个结果会更新max,如果真的大会被记录下来不会丢失。随便移动的话,也不会影响最终结果max。如果两条边都不是最终结果的边,那么两条边迟早都被排除,哪个前哪个后也不影响。

Finally,带有详细注释的代码放在放在我的个人博客http://jinhuaiyu.com/leetcode-container-with-most-water/
希望后面碰到类似的题能想起今天的思想——缩减搜索空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值