Leetcode刷题记录之贪心

455. 分发饼干#

题目描述#

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

对每个孩子 i,都有一个胃口值 children[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 cookies[j] 。如果 cookies[j] >= children[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

题解#

按照孩子的胃口从小到大的顺序依次满足每个孩子,且对于每个孩子,应该选择可以满足这个孩子的胃口且尺寸最小的饼干。

代码#

class Solution {
public:
  int findContentChildren(std::vector<int>& children, std::vector<int>& cookies) {
    std::sort(children.begin(), children.end());
    std::sort(cookies.begin(), cookies.end());
    int children_idx = 0, cookie_idx = 0;
    int children_sz = static_cast<int>(children.size()), cookie_sz = static_cast<int>(cookies.size());
    while (children_idx < children_sz && cookie_idx < cookie_sz) {
      if (children[children_idx] <= cookies[cookie_idx]) {
        ++children_idx;
      }
      ++cookie_idx;
    }
    return children_idx;
  }
};

刷题记录#

一刷 2022年7月28日

135. 分发糖果#

题目描述#

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

题解#

  1. 初始化给每个孩子分一个糖果。

  2. 从左往右遍历,右边的孩子的评分如果高于左边孩子的评分,令右边孩子的糖果数 = 左边孩子的糖果数 + 1。

  3. 从右往左遍历,左边孩子的评分如果高于右边孩子的评分,且左边孩子当前用于的糖果数少于右边孩子,则更新左边孩子的糖果数 = 右边孩子的糖果数 + 1。

代码#

class Solution {
public:
  int candy(std::vector<int>& ratings) {
    int sz = static_cast<int>(ratings.size());
    vector<int> candies(sz, 1);
    for (int i = 1; i < sz; ++i) {
      if (ratings[i] > ratings[i - 1]) {
        candies[i] = candies[i - 1] + 1;
      }
    }
    for (int i = sz - 2; i >= 0; --i) {
      if (ratings[i] > ratings[i + 1]) {
        candies[i] = std::max(candies[i + 1] + 1, candies[i]);
      }
    }
    return std::accumulate(candies.cbegin(), candies.cend(), 0);
  }
};

刷题记录#

一刷 2022年7月29日

435. 无重叠区间#

题目描述#

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回需要移除区间的最小数量,使剩余区间互不重叠 。

输入输出#

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

题解#

求最少的移除区间个数,等价于尽量多保留不重叠的区间。选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间。因此,采取的贪心策略为,优先保留结尾小且不相交的区间。

代码#

class Solution {
public:
  int eraseOverlapIntervals(vector<vector<int>>& intervals) {
    if (intervals.size() <= 1) return 0;
    std::sort(intervals.begin(), intervals.end(), [](auto &x, auto &y) -> bool {
      return x[1] < y[1];
    });
    int res = 0, pre_end = intervals[0][1];
    for (int i = 1; i < intervals.size(); ++i) {
      int cur_start = intervals[i][0], cur_end = intervals[i][1];
      if (cur_start < pre_end) {
        ++res;
      } else {
        pre_end = cur_end;
      }
    }
    return res;
  }
};

刷题记录#

一刷 2022年7月30日

605. 种花问题#

题目描述#

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false

输入输出#

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true

题解#

采取的贪心策略为,见缝插针。只要当前位置满足可以种植的条件,就在当前位置种上花(令 flowerbed[i] = 1 )。在检测当前位置是否可以种花时,首位置和尾位置需要特殊考虑。

代码#

class Solution {
public:
  bool canPlaceFlowers(vector<int>& flowerbed, int n) {
    int sz{static_cast<int>(flowerbed.size())};
    int plant{0};
    for (int i = 0; i < sz; ++i) {
      // test if current position can place a flower
      bool can{flowerbed[i] == 0
                 && (i <= 0 || flowerbed[i - 1] == 0) // i > 0 ? flowerbed[i - 1] == 0 : true
                 && (i >= sz - 1 || flowerbed[i + 1] == 0)}; // i < sz - 1 ? flowerbed[i + 1] == 0 : true
      plant += can;
      // place a flower
      if (can) flowerbed[i] = 1;
    }
    return plant >= n;
  }
};

刷题记录#

一刷 2022年7月30日

452. 用最少数量的箭引爆气球#

题目描述#

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中 points[i] = [xstart, xend] 表示水平直径在 xstartxend 之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstartxend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。

输入输出#

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。

题解#

一个气球最迟的引爆时机是在其右边沿引爆,在气球的右边沿放置弓箭可以最大程度的引爆更多的气球。因此我们遵守一个策略,弓箭必须放置在气球的右边沿。

在第一个气球的右边沿放置第一支弓箭,遍历气球,如果当前的气球无法被上一把弓箭引爆,则需要在当前气球的右边沿重新放置一支弓箭。

代码#

class Solution {
public:
  int findMinArrowShots(vector<vector<int>>& points) {
    int sz = static_cast<int>(points.size());
    if (sz == 0) return 0;
    std::sort(points.begin(), points.end(), [](auto &x, auto &y) -> bool {
      return x[1] < y[1];
    });
    // The first arrow should be placed on the edge of the first balloon
    int arrow = points[0][1], arrow_number = 1;
    for (int i = 1; i < sz; ++i) {
      if (points[i][0] > arrow) { // launch a new arrow
        arrow_number += 1;
        arrow = points[i][1];
      }
    }
    return arrow_number;
  }
};

刷题记录#

一刷 2022年7月30日

763. 划分字母区间#

题目描述#

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

输入输出#

输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。

题解#

遍历字符串,记录每个字母最后一次出现的下标位置到 positions 数组。

记录子序列的起始位置 start 和 结束位置 end ,遍历字符串,每次根据 positions 数组更新结束位置 end,如果当前位置刚好是当前字母所能到达的最远的位置,说明当前序列满足要求。

代码#

class Solution {
public:
  vector<int> partitionLabels(string s) {
    std::vector<int> positions(26, 0);
    std::vector<int> res;
    int sz = static_cast<int >(s.size());
    // 记录每个字母的最远处
    for (int i = 0; i < sz; ++i) {
      positions[s[i] - 'a'] = i;
    }
    int start = 0, end;
    for (int i = 0; i < sz; ++i) {
      end = std::max(positions[s[i] - 'a'], end);
      if (end == i) {
        res.push_back(i - start + 1);
        start = i + 1;
      }
    }
    return res;
  }
};

刷题记录#

一刷 2022年7月30日

122. 买卖股票的最佳时机 II#

题目描述#

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

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

返回 你能获得的 最大 利润 。

输入输出#

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。

题解#

把股票的价格走势画成折线图,为了利益最大化,应该在股票上涨阶段进行买卖,因此统计上升阶段的差值之和就是最大的收益。即我们应该在波谷进行买入,在波峰进行卖出。

代码#

class Solution {
public:
  int maxProfit(vector<int>& prices) {
    int sz = static_cast<int >(prices.size());
    if (sz == 0) return 0;
    int buy_price = prices[0], res = 0;
    for (int i = 1; i < sz; ++i) {
      int left = prices[ i - 1];
      int right = i == sz - 1 ? prices[i] : prices[i + 1];
      // 波谷买入
      if (prices[i] > left && prices[i] >= right) {
        res += prices[i] - buy_price;
      }
      // 波峰卖出
      if (prices[i] <= left && prices[i] < right) {
        buy_price = prices[i];
      }
    }
    return res;
  }
};

刷题记录#

一刷 2022年7月31日

406. 根据身高重建队列#

题目描述#

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

输入输出#

输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。

题解#

先排序:按照 hi 为第一关键字降序,ki 为第二关键字升序进行排序。

按照排完序后的顺序,依次将每个人放入队列中,那么当我们放入第 i 个人时:

0,⋯ ,i−1 个人已经在队列中被安排了正确的位置,无论如何放置第 i 个人,都不会影响当前队列的准确性。

因此我们可以采用「插空」的方法,依次给每一个人在当前的队列中选择一个插入的位置。也就是说,当我们放入第 i 个人时,只需要将其插入队列中,使得他的前面恰好有 ki 个人即可。

代码#

class Solution {
public:
  vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
    std::sort(people.begin(), people.end(), [](auto &x, auto &y) -> bool {
      if (x[0] == y[0]) return x[1] < y[1];
      return x[0] > y[0];
    });
    vector<vector<int>> res;
    for (auto &item : people) {
      res.insert(res.begin() + item[1], item);
    }
    return res;
  }
};

刷题记录#

一刷 2022年7月31日

665. 非递减数列#

题目描述#

给你一个长度为 n 的整数数组 nums ,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

输入输出#

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个 4 变成 1 来使得它成为一个非递减数列。

题解#

当数组出现递减的情况时,即 nums[i] > nums[i + 1],有两种解决方法:

  1. nums[i] 缩小,令 nums[i] == nums[i + 1]
  2. nums[i + 1] 放大,令 nums[i + 1] == nums[i]

如果将使用第一种方法,将 nums[i] 缩小,可能会导致其无法融入前面已经遍历过的非递减子数列;比如 1 4 5 3 。因此,只有当 nums[i + 1] >= nums[i - 1] 时,使用第一种方法才是安全的。

如果将使用第二种方法,将 nums[i + 1] 放大,可能会导致其后续继续出现递减;比如 1 4 6 2 3 4 5 。因此,只有当 nums[i + 2] >= nums[i] 时,使用第二种方法才是安全的。

采取的策略是,尝试是否能安全的使用两种方法。如果两种方法都不适用,直接返回 false

代码#

class Solution {
public:
  bool checkPossibility(vector<int>& nums) {
    int sz = static_cast<int>(nums.size());
    bool changed = false;
    for (int i = 0; i < sz - 1; ++i) {
      // 出现递减的情况
      if (nums[i] > nums[i + 1]) {
        // 已经用掉了唯一的一次修改的机会
        if (changed) return false;
        if (i == 0 || nums[i + 1] >= nums[i - 1]) { // 尝试第一种方案
          nums[i] = nums[i + 1];
          changed = true;
        } else if (i == sz - 2 || nums[i + 2] >= nums[i]) { // 尝试第二种方案
          nums[i + 1] = nums[i];
          changed = true;
        } else {
          return false;
        }
      }
    }
    return true;
  }
};

刷题记录#

一刷 2022年7月31日

作者:lmcoding

出处:https://www.cnblogs.com/lmcoding/p/16565502.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   cclemontree  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示