Java贪心算法——leetcode中等题

目录

1、盛最多水的容器

题目要求

示例

 题目分析

​ 代码演示

 总结

2、跳跃游戏Ⅰ

题目要求

示例

题目分析

代码演示

 总结

3、跳跃问题Ⅱ

题目要求

示例

题目分析

代码演示

总结


1、盛最多水的容器

题目要求

        给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和           (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例:

输入:[1,8,6,2,5,4,8,3,7]

输出:49

解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

 题目分析

        设面积为area,设左下标为l,右下标为r。由图可知,假设height[r] < height[l],则面积的计算公式为:

area = height[r] * (r - l)

        在遇到有两个变量在一个数组中时,可以考虑使用双指针。创建两个指针 l 和 r,一个代表左下标,一个代表右下标,从数组的两边开始遍历。由于长方形的高由小的那条边决定,因此可以让 r 和 l 所对应的数组的值进行比较,小的一方的下标向内移动一位,再计算面积的值。与移动前相比,若面积更大,则取移动后的面积,若更小,则取移动前的面积,随后再重复以上操作直到       l > r 。如图所示:

        l 和 r 从数组两边开始遍历,计算每一次遍历的面积。

        l < r ,l 向右移动一位,计算面积的值,与移动前的面积进行比较。

 代码演示

    //双指针,总是移动数字较小的那个指针
    public static int maxArea2(int[] height) {
        int l = 0, r = height.length - 1;
        int ans = 0;
        while (l < r) {
            int area = Math.min(height[l], height[r]) * (r - l);
            ans = Math.max(ans, area);
            if (height[l] <= height[r]) {
                ++l;
            }
            else {
                --r;
            }
        }
        return ans;
    }

 总结:

        将面积问题分解成边的大小问题,遇到数组与两个变量,优先考虑双指针+贪心。

2、跳跃游戏Ⅰ

题目要求

        给定一个非负整数数组 nums ,你最初位于数组的第一个下标。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。

示例:

输入:nums = [2,3,1,1,4]

输出:true

解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

题目分析

        数组中的元素代表可跳跃的最大长度,若计算每一次的跳跃地点,需要根据元素的值来划定遍历的范围,然后遍历范围内所有可跳跃的地点,通过比较所能到达的最远距离来确定最佳的跳跃地点。这种做法非常繁琐。

        不妨换个想法,若当前的点最远可以到达的位置大于等于数组中的最后一个位置,说明可以到达。那么我们每次都只跳一格,计算当前所能到达的最远距离,比跳跃之前大,则更新最远可以到达的位置,当大于等于数组中的最后一个位置时返回true。

代码演示

    //正确思路
    /**
     * 定义一个cover,表示当前元素能跳的最大覆盖范围,每次我都只往右跳一格
     * 然后更新cover范围,将当前索引的覆盖范围和上一次的覆盖范围cover相比,
     * 两者中的最大值就是更新后的cover。当最大范围>=数组最后一个索引时,返回true
     */
    public static boolean canJump1(int[] nums) {
        int cover = 0;
        int num = nums.length-1;
        for (int i = 0; i <= cover; i++) {
            cover = Math.max(cover,i+nums[i]);
            if(cover >= num){
                return true;
            }
        }
        return false;
    }

 总结:

        做该题时如果直观的思想实现起来太麻烦,可以尝试改变一下需要考虑的对象。从考虑跳跃的坐标变成考虑跳跃的最远距离。

3、跳跃问题Ⅱ

题目要求

        给你一个非负整数数组 nums ,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。假设你总是可以到达数组的最后一个位置。

示例:

输入: nums = [2,3,1,1,4]

输出: 2

解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

题目分析

       由题可得,我们需要计算出跳跃到终点的次数,但这并不意味着需要计算出每次跳跃最合适的地点,因为非常繁琐,需要考虑诸多因素。因此和“跳跃问题Ⅰ”一样,我们同样考虑最远距离。

        由于目标是使用最少的跳跃次数到达数组的最后一个位置,那么可以依次遍历数组内的每一个元素,遍历时计算他们所能到达最远的位置,即可以到达的最远下标,然后进行比较。若比上一个元素跳得远,则更新最远距离。当我们遍历到当前最远距离所在的下标时,意味着我们跳了一次。

代码演示

    public static int jump(int[] nums) {

        int count = 0;
        int jump = 0;
        int farthest = 0;

        //如果访问最后一个元素,在边界正好为最后一个位置的情况下,会增加一次「不必要的跳跃次数」,例如{0},因此不必访问最后一个元素。
        for (int i = 0; i < nums.length-1; i++) {
            farthest = Math.max(farthest,i + nums[i]);
            if(i == jump) {
                jump = nums[farthest];
                count++;
            }
        }
        return count;
    }

总结:

        这道题是典型的贪心算法,通过局部最优解得到全局最优解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梅川库紫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值