代码随想录day30打卡|| 452用最少数量的箭引爆气球 435无重叠区间 763划分字母区间

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

力扣题目链接

题目描述:

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

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

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

 

示例 1:

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

示例 2:

输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。

示例 3:

输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:气球可以用2支箭来爆破:
- 在x = 2处发射箭,击破气球[1,2]和[2,3]。
- 在x = 4处射出箭,击破气球[3,4]和[4,5]。

代码:

class Solution {  
public:  
    // 定义一个比较函数 cmp,用于在排序时比较两个气球的起始点  
    static bool cmp(const vector<int> &a, const vector<int> &b) {  
        return a[0] < b[0]; // 根据气球的起始坐标进行升序排序  
    }  

    // 主函数,负责计算需要的最小箭数  
    int findMinArrowShots(vector<vector<int>>& points) {  
        // 如果气球数组为空,则需要的箭数为0  
        if (points.size() == 0) return 0;  

        // 根据气球的起始坐标对气球进行排序  
        sort(points.begin(), points.end(), cmp);  

        // 初始化结果为1,因为至少需要一支箭来击中第一个气球  
        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][1], points[i][1]);  
            }  
        }  
        
        // 返回所需的最小箭数  
        return result;  
    }  
};  

代码分析:

  1. 排序气球: 首先对气球的起始位置进行排序,这样可以方便地检查气球之间是否存在重叠。

  2. 初始化箭数: result 用于统计最小箭数,初始设为 1,因为至少需要一支箭来击中最左侧的气球。

  3. 遍历比较:

    • 检查当前气球是否与前一个气球重叠。
    • 如果不重叠(即当前气球的起始点大于前一个气球的结束点),则需要增加一支箭。
    • 如果重叠,则更新当前气球的结束位置为与前一个气球重叠部分的最小结束点。这是因为一支箭可以同时击中所有重叠的气球,从而减少箭的数量。
  4. 返回结果: 遍历完所有气球后,返回所需的最小箭数 result

这个逻辑确保算法的时间复杂度为 O(n log n),主要是由于排序过程,而遍历气球的过程时间复杂度为 O(n)。

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

力扣题目链接

题目描述:

给定一个区间的集合 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){
        return a[0]<b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size()==0)return 0;
        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]){
                continue;
            }else{
                //这题与上一题的不同之处就是更新边界的时候result++因为更新边界的时候相当于移除了 
                  这个重叠的区间
                intervals[i][1]=min(intervals[i][1],intervals[i-1][1]);
                result++;
            }
        }
        return result;
    }
};

//这题与上一题的不同之处就是更新边界的时候result++因为更新边界的时候相当于移除了这个重叠的区间

可以改变if里的条件,不仅是语法,也是另一个if逻辑

class Solution {
public:
    static bool cmp(vector<int> &a,vector<int> &b){
        return a[0]<b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size()==0)return 0;
        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;
    }
};

官方代码:(我没看这个,等有时间想不开了再来看)

class Solution {
public:
    // 按照区间右边界排序
    static bool cmp (const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 1; // 记录非交叉区间的个数
        int end = intervals[0][1]; // 记录区间分割点
        for (int i = 1; i < intervals.size(); i++) {
            if (end <= intervals[i][0]) {
                end = intervals[i][1];
                count++;
            }
        }
        return intervals.size() - count;
    }
};

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) {  
        // 创建一个数组 hush,用于记录每个字母最后出现的位置  
        int hush[27] = {0}; // 26个字母 + 1 作为补位  

        // 遍历字符串,记录每个字母在s中最后出现的索引  
        for (int i = 0; i < s.size(); i++) {  
            hush[s[i] - 'a'] = i; // 使用 'a' 的ASCII值作为偏移量,将字母映射到数组索引  
        }  

        // 初始化右边界和左边界  
        int right = 0; // 目前找到的右边界  
        int left = 0;  // 当前片段的左边界  
        vector<int> result; // 用于存储每个片段的长度  

        // 遍历字符串,计算每个片段的长度  
        for (int i = 0; i < s.size(); i++) {  
            // 更新当前字母的右边界  
            right = max(right, hush[s[i] - 'a']); // 取当前字母最后出现位置和当前右边界中的最大值  
            
            // 如果当前位置i达到了当前右边界,说明可以确定一个片段  
            if (i == right) {  
                // 将当前片段长度加入结果  
                result.push_back(right - left + 1); // 计算当前片段的长度,并加入result  
                left = right + 1; // 更新左边界,继续寻找下一个片段  
            }  
        }  

        // 返回所有片段的长度  
        return result;  
    }  
}; 

代码分析:

  1. 记录字符最后出现位置:

    • 使用一个大小为 27 的整型数组 hush 来记录每个字母最后出现的位置(下标为 0 - 25 代表字母 a - z,补位为 0)。
  2. 遍历字符串获取索引:

    • 第一个 for 循环遍历字符串 s,对每个字符计算其在字符串中最后出现的位置并存储在 hush 数组中。
  3. 初始化边界指针:

    • right 用于表示当前片段可以包含的最右字符位置,left 用于表示当前片段的最左边界。
  4. 确定片段:

    • 第二个 for 循环遍历字符串,通过更新 right 指针以找到片段的结束位置。
    • 每当 i 到达 right,即当前索引等于当前片段的右边界,说明可以确定一个完整片段的长度。
    • 使用 result.push_back(right - left + 1) 计算并记录这个片段的长度,更新 left 为 right + 1,以便查找下一个片段。
  5. 返回结果:

    • 最后返回包含所有片段长度的 result 向量。

这个算法的时间复杂度是 O(n),其中 n 是字符串 s 的长度。因为我们最多遍历字符串两次(一次记录字符最后的位置,一次划分片段)。

  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值