贪心算法・下篇:区间与字符串大闯关,4 道进阶题解锁高阶玩法

一、贪心算法进阶概述

入门篇我们掌握了 "匹配型""累加型" 的基础贪心,进阶篇将聚焦区间调度、字符串分割、环形问题等更复杂的场景。

这类题目的贪心策略往往隐藏更深,通常需要先对数据排序,再通过维护 "当前边界" 来做决策。核心套路是:排序定顺序,边界做贪心


二、4 道 C++ 进阶实战例题

例 1:无重叠区间(LeetCode 435)

题目描述:给定一个区间集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

贪心策略 :按区间的结束位置从小到大排序。每次选择结束最早的区间保留,给后续区间留出更多空间,这样就能保留最多的不重叠区间,移除数量自然最少。

cpp

运行

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int eraseOverlapIntervals(vector<vector<int>>& intervals) {
    if (intervals.empty()) return 0;
    
    // 按区间结束位置升序排序
    sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    });
    
    int count = 1;      // 保留的区间数,至少保留第一个
    int end = intervals[0][1]; // 当前最后一个保留区间的结束点
    
    for (int i = 1; i < intervals.size(); i++) {
        // 当前区间起点 >= 上一个保留区间终点,无重叠,保留
        if (intervals[i][0] >= end) {
            end = intervals[i][1];
            count++;
        }
    }
    // 总区间数 - 最多保留数 = 最少移除数
    return intervals.size() - count;
}

int main() {
    vector<vector<int>> intervals = {{1,2}, {2,3}, {3,4}, {1,3}};
    cout << "最少移除区间数:" << eraseOverlapIntervals(intervals) << endl;
    return 0;
}

例 2:用最少数量的箭引爆气球(LeetCode 452)

题目描述:气球在水平数轴上分布,每个气球对应一个区间。弓箭从 x 轴垂直射出,可以引爆所有覆盖到的气球。求引爆所有气球最少需要多少支箭。

贪心策略:按气球右边界排序。第一支箭射在第一个气球的最右端,尽可能覆盖更多后续气球;遇到覆盖不到的气球时,新增一支箭,并更新射箭位置为当前气球最右端。

cpp

运行

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int findMinArrowShots(vector<vector<int>>& points) {
    if (points.empty()) return 0;
    
    // 按右边界升序排序
    sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    });
    
    int arrows = 1;
    int pos = points[0][1]; // 第一支箭的位置
    
    for (int i = 1; i < points.size(); i++) {
        // 当前气球左边界 > 射箭位置,射不到,需要新箭
        if (points[i][0] > pos) {
            arrows++;
            pos = points[i][1]; // 新箭射在当前气球最右端
        }
    }
    return arrows;
}

int main() {
    vector<vector<int>> points = {{10,16}, {2,8}, {1,6}, {7,12}};
    cout << "最少需要箭数:" << findMinArrowShots(points) << endl;
    return 0;
}

例 3:划分字母区间(LeetCode 763)

题目描述:把字符串划分为尽可能多的片段,同一个字母只出现在其中一个片段里。返回每个片段的长度列表。

贪心策略:先遍历一次字符串,记录每个字母最后一次出现的位置。再遍历字符串,不断扩展当前片段的右边界(取当前字母最后位置的最大值);当遍历到右边界时,完成一个片段的分割。

cpp

运行

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;

vector<int> partitionLabels(string s) {
    int last[26] = {0};
    // 记录每个字母最后出现的下标
    for (int i = 0; i < s.size(); i++) {
        last[s[i] - 'a'] = i;
    }
    
    vector<int> result;
    int start = 0; // 当前片段起点
    int end = 0;   // 当前片段终点
    
    for (int i = 0; i < s.size(); i++) {
        // 贪心扩展终点:取当前字母最后位置和原终点的较大值
        end = max(end, last[s[i] - 'a']);
        // 遍历到终点,完成一个片段
        if (i == end) {
            result.push_back(end - start + 1);
            start = i + 1; // 更新下一个片段起点
        }
    }
    return result;
}

int main() {
    string s = "ababcbacadefegdehijhklij";
    vector<int> res = partitionLabels(s);
    cout << "各片段长度:";
    for (int len : res) cout << len << " ";
    return 0;
}

例 4:加油站(LeetCode 134)

题目描述 :环形路线上有 n 个加油站,第 i 个加油站有汽油 gas[i],从第 i 站开到第 i+1 站消耗汽油 cost[i]。判断能否绕环路行驶一周,返回出发加油站编号,无解返回 - 1。

贪心策略

  1. 总油量 ≥ 总消耗,则一定存在解;
  2. 遍历过程中若当前剩余油量为负,说明从起点到当前站的所有站都不能作为起点,起点直接跳到下一站。

cpp

运行

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
    int totalSum = 0;  // 全程总剩余油量
    int curSum = 0;    // 当前段剩余油量
    int start = 0;     // 起点
    
    for (int i = 0; i < gas.size(); i++) {
        totalSum += gas[i] - cost[i];
        curSum += gas[i] - cost[i];
        
        // 当前段剩余油量为负,说明起点到i都不能作为起点
        if (curSum < 0) {
            start = i + 1; // 起点更新为下一站
            curSum = 0;    // 重置当前段剩余
        }
    }
    // 总油量不够则无解,否则返回起点
    return totalSum >= 0 ? start : -1;
}

int main() {
    vector<int> gas = {1, 2, 3, 4, 5};
    vector<int> cost = {3, 4, 5, 1, 2};
    cout << "起点加油站编号:" << canCompleteCircuit(gas, cost) << endl;
    return 0;
}

谢谢

相关推荐
Omics Pro2 分钟前
1个月2轮融资!长寿虚拟细胞
数据库·人工智能·算法·机器学习·自然语言处理
residual_fan2 分钟前
航空发动机故障诊断专用智能体(六):实际机队健康管理中的应用考量与展望
人工智能·算法·数据挖掘·数据分析
码匠许师傅12 分钟前
【C++三方组件】cxxopts:轻量首选的命令行解析
c++
hansang_IR24 分钟前
【讲解】CSP-S2026 第一轮初赛
c++·算法
hanlin0327 分钟前
刷题笔记:力扣第76题-最小覆盖子串
笔记·算法·leetcode
Zane199428 分钟前
比较排序跑不出O(nlogn)是数学上证明过的,计数、桶、基数排序凭什么能绕开这堵墙
算法·排序算法
tiger86530 分钟前
大语言模型面试和题解,梳理中国主流开源 LLM 系列的发展脉络、技术路线与工程取舍
人工智能·gpt·深度学习·算法·自然语言处理·面试·transformer
Methy33 分钟前
IoTHub半个月崩了五次?都是裸rethrow惹的祸
服务器·c++·后端
我不会起名字32242 分钟前
一天一道力扣Hot100(36):深度优先算法---组合总和
数据结构·c++·后端·python·算法·go
纪念 2291 小时前
c++类和对象(四)
开发语言·c++