算法刷题-贪心入门(分发饼干 + 摆动序列 + 最大子数组和 + 买卖股票的最佳时机Ⅱ + 跳跃游戏)

110 阅读5分钟

算法刷题博客封面.png

题目 1 - 分发饼干

简介

题目 - 01: 分发饼干 (LeetCode 455)

题目 - 02: 摆动序列 (LeetCode 376)

题目 - 03: 最大子序和 (LeetCode 53)

题目 - 04: 买卖股票的最佳时机Ⅱ (LeetCode 122)

题目 - 05: 跳跃游戏 (LeetCode 55)

原题连接

LeetCode 455 - 分发饼干

题目描述

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼 干 j,都有一个尺寸 s[j]

如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i,这个孩子会得到满足。你的目标是满足尽可能多的孩子,并输出这个最大数值。

思路

  • 遍历小孩和饼干,都按从大到小优先匹配
  • 优先将 大饼干 分配给 胃口大的孩子
  • 如果该孩子可以被喂饱
  • 可喂饱的数目 + 1

匹配条件:当前饼干刚好满足学生胃口,或者大于等于学生胃口. 匹配后遍历下一个饼干

如果无法匹配,继续遍历下一个学生

代码实现

class Solution {

    public int findContentChildren(int[] g, int[] s) {
        int m = g.length; // 小孩
        int n = s.length; // 饼干

        Arrays.sort(g); // 将小孩按照胃口从小到大排序
        Arrays.sort(s); // 将饼干值按照从小到大排序

        int sIdx = n - 1; // 遍历饼干,从大到小
        int cnt = 0;
        /*
           遍历小孩和饼干,都按从大到小优先匹配
           匹配条件:当前饼干刚好满足学生胃口,或者大于等于学生胃口. 匹配后遍历下一个饼干
           如果无法匹配,继续遍历下一个学生
         */
        for (int i = m - 1; i >= 0; i--) {
            //
            if (sIdx >= 0 && s[sIdx] >= g[i]) {
                cnt++;
                sIdx--; // 下一个饼干
            }
        }
        return cnt;
    }
}

执行结果

  • 时间复杂度: O(NlogN)
  • 空间复杂度:O(1)

题目 2 - 摆动序列

原题连接

LeetCode 376 - 摆动序列

题目描述

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅 有一个元素 或者含 两个不等元素 的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5][1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列: 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

思路

  • 要保证数组摆动,则相邻元素需要互为峰值,一个朝上一个朝下
  • 对于长度小于 2 的序列,直接返回长度为 1
  • 起始假设最右端始终为一个峰值
  • 判断是否为一个峰值的条件:
  1. 前一次元素差值 记录 preDiff>= 0 当前对元素的差值 currDiff < 0; (即上一个峰值朝上,此时查询到朝下峰值)
  2. 前一次元素差值记录 preDiff <= 0, 当前对的元素的差值 currDiff > 0; (即上一个峰值朝下,此时查询到朝上峰值)
  • 起始的时候差值为 0; 因此最少记录长度为 2

代码实现

    public int wiggleMaxLength(int[] nums) {
        if(nums.length < 2) {
            return 1;
        }
        int ans = 1;    //起始长度为 1
        int preDiff = 0;    //上一对元素的差值
        int currDiff = 0;   //当前对元素的差值
        for(int i = 1; i < nums.length; i++) {
            currDiff = nums[i] - nums[i - 1];   //当前差值
            if((preDiff <= 0 && currDiff > 0) || (preDiff >= 0 && currDiff < 0)) {
                ans++;      //更新序列长度
                preDiff = currDiff;   //更新前一对峰值; 
            } 
        }
        return ans;
    }

执行结果

  • 时间复杂度: O(N)
  • 空间复杂度:O(1)

题目 3 - 最大子数组和

原题连接

LeetCode 53 - 最大子数组和

题目描述

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

思路

  • 设区间总和为 sum
  • 分两种情况:
  1. 如果 sum >= 0, 则累加下一个元素可能得到更大值
  2. 如果此时 sum < 0 已经成立,此区间和即使加上后序和为正数的区间和,最大值也是小于 统计正数的区间和;因此直接更新为 sum = 0 后再记录
  • 即当 sum < 0 的时候,重置 sum = 0 后累加后续区间;如果 sum >= 0 还成立,继续尝试累加后续区间

代码实现

class Solution {

    public int maxSubArray(int[] nums) {
        int sum = nums[0];
        int max = sum; // 记录最大值
        for (int i = 1; i < nums.length; i++) {
            if (sum < 0) { // 一个负数会导致和变小
                sum = 0;
            }
            sum += nums[i];
            max = Math.max(sum, max);

        }
        return max;
    }
}

执行结果

  • 时间复杂度: O(N)
  • 空间复杂度:O(1)

题目 4 - 买卖股票的最佳时机 Ⅱ

原题链接

LeetCode 122 - 买卖股票的最佳时机Ⅱ

题目描述

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否 购买和/或出售股票 。你在任何时候 最多 只能持有 一股 股票。

你也可以先购买,然后在 同一天 出售。 返回 你能获得的 最大 利润 。

思路

  • 想要利润最大,必须保证每笔隔 ? 天差价总和正数,每笔交易的差价也为正数
  • 由于可以当天买卖,因此统计所有升区间,即 prices[i] - prices[i - 1] > 0 的总和; 即为最大利润

代码实现

class Solution {

    public int maxProfit(int[] prices) {
        int max = 0;
        for(int i = 1; i < prices.length; i++) {
            //大于 0 即记录
            max += Math.max(prices[i] - prices[i - 1], 0);
        }
        return max;
    }
}

执行结果

  • 时间复杂度: O(N)
  • 空间复杂度:O(1)