牛客题解 | 盛水最多的容器

题目## 题目

题目链接

题目主要信息:
  • 输入一个数组,其中每个元素代表水桶边界高度
  • 水桶容积为边界较短的一边高度乘上两边界的距离(数组下标表示距离)
  • 求在数组中选取两个边,求最大容积
举一反三:

学习完本题的思路你可以解决如下题目:

BM94. 接雨水问题

方法:贪心法(建议使用)

知识点1:双指针

双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个指针(特殊情况甚至可以多个),两个指针或是同方向访问两个链表、或是同方向访问一个链表(快慢指针)、或是相反方向扫描(对撞指针),从而达到我们需要的目的。

知识点2:贪心思想

贪心思想属于动态规划思想中的一种,其基本原理是找出整体当中给的每个局部子结构的最优解,并且最终将所有的这些局部最优解结合起来形成整体上的一个最优解。

思路:

这道题利用了水桶的短板原理,较短的一边控制最大水量,因此直接用较短边长乘底部两边距离就可以得到当前情况下的容积。但是要怎么找最大值呢?

可以利用贪心思想:我们都知道容积与最短边长和底边长有关,与长的底边一定以首尾为边,但是首尾不一定够高,中间可能会出现更高但是底边更短的情况,因此我们可以使用对撞双指针向中间靠,这样底边长会缩短,因此还想要有更大容积只能是增加最短变长,此时我们每次指针移动就移动较短的一边,因为贪心思想下较长的一边比较短的一边更可能出现更大容积。

//优先舍弃较短的边
if(height[left] < height[right]) 
    left++;
else
    right--;

具体做法:

  • step 1:优先排除不能形成容器的特殊情况。
  • step 2:初始化双指针指向数组首尾,每次利用上述公式计算当前的容积,维护一个最大容积作为返回值。
  • step 3:对撞双指针向中间靠,但是依据贪心思想,每次指向较短边的指针向中间靠,另一指针不变。

图示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java代码实现:

import java.util.*;
public class Solution {
    public int maxArea (int[] height) {
        //排除不能形成容器的情况
        if(height.length < 2) 
            return 0;
        int res = 0; 
        //双指针左右界
        int left = 0; 
        int right = height.length - 1;
        //共同遍历完所有的数组
        while(left < right){ 
            //计算区域水容量
            int capacity = Math.min(height[left], height[right]) * (right - left); 
            //维护最大值
            res = Math.max(res, capacity); 
            //优先舍弃较短的边
            if(height[left] < height[right]) 
                left++;
            else
                right--;
         }
        return res;
    }
}

C++代码实现:

class Solution {
public:
    int maxArea(vector<int>& height) {
        //排除不能形成容器的情况
        if(height.size() < 2) 
            return 0;
        int res = 0; 
        //双指针左右界
        int left = 0; 
        int right = height.size() - 1;
        //共同遍历完所有的数组
        while(left < right){ 
            //计算区域水容量
            int capacity = min(height[left], height[right]) * (right - left);
            //维护最大值 
            res = max(res, capacity); 
            //优先舍弃较短的边
            if(height[left] < height[right]) 
                left++;
            else
                right--;
         }
        return res;
    }
};

Python实现代码:

class Solution:
    def maxArea(self , height: List[int]) -> int:
        #排除不能形成容器的情况
        if len(height) < 2: 
            return 0
        res = 0
        #双指针左右界
        left = 0 
        right = len(height) - 1
        #共同遍历完所有的数组
        while left < right:
            #计算区域水容量
            capacity = min(height[left], height[right]) * (right - left) 
            #维护最大值
            res = max(res, capacity) 
            #优先舍弃较短的边
            if height[left] < height[right]: 
                left += 1
            else:
                right -= 1
        return res

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),双指针共同遍历一次数组
  • 空间复杂度: O ( 1 ) O(1) O(1),常数级变量,没有额外辅助空间

题目链接

#描述

这是一道对栈和队列之间灵活转化的题目。
难度:一星
考察知识:队列,栈

#题解
##方法:模拟

如果我知道队列是FIFO,栈是FILO,但是这道题我还是不知道怎么写怎么办?
对于这种感觉不难,但是又不会写的,方法就是模拟。
比如有如下操作:(pop操作确保栈中有元素)

push(1);push(2);pop(3);push(4);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
根据队列的特性,只能pop(1),pop(2),pop之后的结果
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述是队列的操作。
当push的时候,我们必须要用一个stack来存,假设用stack1来存。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
那么push操作解决了。那么pop操作怎么办呢?
如果pop(1),但是此时在stack1的栈底,如果要pop,必须再将stack1中的数据push到stack2中,然后在pop,如图
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样直接弹出stack2的栈顶就可以了。
如果要继续pop,那就继续弹出stack2就可以了


但是现在总感觉哪里还是有点问题。如果是这样就继续测试几个例子。
如果push(5),
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以最后总结一下:push操作就直接往stack1中push, pop操作需要分类一下:如果stack2为空,那么需要将stack1中的数据转移到stack2中,然后在对stack2进行pop,如果stack2不为空,直接pop就ok。

##复杂度分析
时间复杂度:push操作为O(1),pop操作为O(1)
空间复杂度:需要stack来存,O(n)
##代码

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int ret = stack2.top();
        stack2.pop();
        return ret;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值