贪心算法 简单三题

452.用最少的数量的箭引爆气球(贪心算法)

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

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

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

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

class Solution {
public:
    static bool cmp(vector<int>&a,vector<int>&b){
        return a[0]<b[0];
    }
    int findMinArrowShots(vector<vector<int>>& points) {
        sort(points.begin(),points.end(),cmp);
        int result=1;
        for(int i=1;i<points.size();i++){
            //如果当前的气球位置最小值 大于 上一个气球位置的最大值 说明两个气球不重合 需要加一只弓箭
            if(points[i][0]>points[i-1][1]){
                result++;
            }else{
            //否则当前两个气球重合,选择两个气球中 最小的最大边界,然后将两个气球看作一个气球,继续进行
                points[i][1]=min(points[i][1],points[i-1][1]);
            }
        }
        return result;
    }
};

1.整体思想

射爆气球有两种情况

1.当两个气球不重合的情况,需要加一只弓箭

2.当两只气球重合的时候,选择其中一只右边界最小的情况,将两个气球看作一个气球,用于下一次的迭代

 

2.逐步分析

 

代码中的 findMinArrowShots 函数实现了这个算法:

  • sort(points.begin(), points.end(), cmp):这行代码使用自定义的比较函数 cmp 对 points 进行排序,确保气球的左边界是递增的。
  • int result = 1;:初始化结果为 1,因为至少需要一支箭。
  • for(int i=1; i<points.size(); i++):遍历排序后的气球数组。
  • if(points[i][0] > points[i-1][1]):检查当前气球是否与前一个气球重合。
  • result++;:如果不重合,需要额外的箭,因此结果加一。
  • else:如果重合,选择两个气球中右边界较小的那个。
  • points[i][1] = min(points[i][1], points[i-1][1]);:更新当前气球的右边界。
  • return result;:返回所需的最少箭数。

这个算法的时间复杂度是 O(nlogn),其中 n 是气球的数量,主要是因为排序操作。空间复杂度是 O(1),除了输入数组外没有使用额外的空间。

 

435.无重叠区间(贪心算法)

435.无重叠区间

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

示例 1:

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

示例 2:

输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了
class Solution {
public:
    static bool cmp(vector<int>&a,vector<int>&b){
        if(a[0]==b[0])return a[1]<b[1];
        return a[0]<b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);
        int result=0;
     //当前值的左边界小于前一个值的右边界时,两个集合重叠
        for(int i=1;i<intervals.size();i++){
            if(intervals[i][0]<intervals[i-1][1]){
            //重叠之后,将两个集合的右边界选一个最小的右边界(删除重叠的集合),防止下次迭代的时候误判
                intervals[i][1]=min(intervals[i][1],intervals[i-1][1]);
                result++;
            }
        }
        return result;
    }
};

1.整体思想

集合重叠的情况是当前集合的左边界小于前一个集合的右边界,此时两个集合重叠,result++,删除重合的集合(选取其中集合的最小右边界),进行下次迭代。

 

2.逐步分析

算法的核心思想也是贪心算法。首先,我们将所有区间按照开始位置进行排序,如果开始位置相同,则按照结束位置进行排序。然后,我们从左到右遍历排序后的区间,如果当前区间的开始位置小于前一个区间的结束位置,这意味着两个区间重叠,我们需要移除一个区间。为了最小化移除的数量,我们选择移除结束位置较大的区间,因为这样可以留下更多的空间给后续的区间。

代码中的 eraseOverlapIntervals 函数实现了这个算法:

  • sort(intervals.begin(), intervals.end(), cmp):这行代码使用自定义的比较函数 cmp 对 intervals 进行排序,确保区间的开始位置是递增的,如果开始位置相同,则结束位置也是递增的。
  • int result = 0;:初始化结果为 0,表示需要移除的区间数量。
  • for(int i=1; i<intervals.size(); i++):遍历排序后的区间数组。
  • if(intervals[i][0] < intervals[i-1][1]):检查当前区间是否与前一个区间重叠。
  • intervals[i][1] = min(intervals[i][1], intervals[i-1][1]);:如果重叠,选择结束位置较小的区间,并将其结束位置更新为两个区间结束位置的最小值。这实际上是模拟移除结束位置较大的区间。
  • result++;:每次更新区间结束位置时,都意味着我们移除了一个区间,因此结果加一。
  • return result;:返回需要移除的最小区间数量。

这个算法的时间复杂度是 O(nlogn),其中 n 是区间的数量,主要是因为排序操作。空间复杂度是 O(1),除了输入数组外没有使用额外的空间。

 

763.划分字母区间(贪心算法)

763.划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。

 

示例 1:

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

示例 2:

输入:s = "eccbbbbdec"
输出:[10]
class Solution {
public:
    vector<int> partitionLabels(string s) {
        vector<int> last(26);
        for(int i=0;i<s.size();i++){
            //实时更新当前元素的最远位置  //s[i]-'a'  s中的元素在last的索引
            last[s[i]-'a']=i;
        }
        vector<int> que;
        int star=0;
        int end=0;
        for(int i=0;i<s.size();i++){
            end=max(end,last[s[i]-'a']);
            if(i==end){
                que.push_back(end-star+1);
                star=end+1;
            }
        }
        return que;
    }
};

 

  • int length = s.size(); 获取字符串 s 的长度并存储在变量 length 中。
  • for (int i = 0; i < length; i++) { ... } 是一个循环,遍历字符串 s 中的每个字符。
  • last[s[i] - 'a'] = i; 这行代码是关键。它计算当前字符在 last 数组中的索引,并将其最后一次出现的位置(即索引 i)存储在对应的位置上。这里的 s[i] - 'a' 是因为英文字母 ‘a’ 到 ‘z’ 可以用数字 0 到 25 来表示,所以我们通过减去 ‘a’ 来将字符转换为对应的索引。

例如,如果字符串 s 中字符 ‘c’(对应索引为 2)最后一次出现的位置是 10,那么 last[2] 将被赋值为 10。

int length = s.size();
for (int i = 0; i < length; i++) {
    last[s[i] - 'a'] = i;
}

这个 last 数组用于后续的分割字符串操作,以便我们可以快速查找任何字符在字符串中的最后一次出现的位置。这是解决“分割字符串”问题的关键步骤,因为它允许我们确定每个分割片段的最小长度,以确保每个字母在整个字符串中只出现在一个片段中。

 vector<int> que;
        int star=0;
        int end=0;
        for(int i=0;i<s.size();i++){
            end=max(end,last[s[i]-'a']);
            if(i==end){
                que.push_back(end-star+1);
                star=end+1;
            }
        }

这段代码的功能是遍历字符串s使用一个队列que来存储字符串中每个字符最后出现位置的最大值。这里,last数组存储了每个字母最后出现的位置。代码的关键在于通过end变量记录当前字符集的结束位置,而star记录开始位置。i等于end时,意味着当前字符集遍历完成,此时将字符集的长度加入队列que中,然后更新starend+1,表示新的字符集开始。这个队列que最终会存储字符串s中每个不同字符集的长度。

 

 

当涉及到背包问题时,贪心算法是一种常用的解决方法。在C语言中,可以使用贪心算法来解决背包问题。下面是一个简单的C语言贪心算法背包问题的代码示例: ```c #include <stdio.h> // 定义物品结构体 typedef struct { int weight; // 物品重量 int value; // 物品价值 } Item; // 贪心算法背包问题函数 void knapsack(Item items[], int n, int capacity) { // 计算物品的性价比(价值除以重量) float ratios[n]; for (int i = 0; i < n; i++) { ratios[i] = (float) items[i].value / items[i].weight; } // 按照性价比排序物品 for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (ratios[j] < ratios[j + 1]) { // 交换物品的位置 float tempRatio = ratios[j]; ratios[j] = ratios[j + 1]; ratios[j + 1] = tempRatio; Item tempItem = items[j]; items[j] = items[j + 1]; items[j + 1] = tempItem; } } } // 装入背包 int currentWeight = 0; float totalValue = 0; for (int i = 0; i < n; i++) { if (currentWeight + items[i].weight <= capacity) { // 将物品放入背包 currentWeight += items[i].weight; totalValue += items[i].value; } else { // 物品只能部分放入背包 int remainingWeight = capacity - currentWeight; totalValue += ratios[i] * remainingWeight; break; } } // 输出结果 printf("背包中物品的总价值为: %.2f\n", totalValue); } int main() { // 创建物品数组 Item items[] = { {10, 60}, {20, 100}, {30, 120} }; int n = sizeof(items) / sizeof(items[0]); // 物品数量 int capacity = 50; // 背包容量 // 调用贪心算法背包问题函数 knapsack(items, n, capacity); return ; } ``` 这段代码实现了一个简单的贪算法背包问题。它首先计算每个物品性价比,然后按照性价比对物品进行排序。接下来,它从性价比最高的物品开始,依次将物品放入背包,直到背包装满或者所有物品都放入背包。最后,输出背包中物品的总价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值