数据结构与算法学习(二)

文章目录

  • [1 贪心算法 (Greedy Algorithm)](#1 贪心算法 (Greedy Algorithm))
    • [1.1 核心思想与特点](#1.1 核心思想与特点)
    • [1.2 经典贪心类问题](#1.2 经典贪心类问题)
    • [1.3 经典问题详解与代码实现](#1.3 经典问题详解与代码实现)

1 贪心算法 (Greedy Algorithm)

  • 贪心算法是一种在每一步选择中都采取当前状态下最优(最有利)的选择,从而希望导致结果是全局最优的算法策略。

1.1 核心思想与特点

  • 核心特征:
    1. 局部最优选择: 每一步都做出当前看来最好的选择
    2. 不可回溯: 一旦做出选择,就不再回退
    3. 无后效性: 当前的选择不会影响后续子问题的结构
  • 贪心算法的适用条件:
    • 贪心性质选择: 局部最优能导致全局最优
    • 最优子结构: 问题的最优解包含子问题的最优解

1.2 经典贪心类问题

  1. 区间调度类问题
  2. 分配类问题
  3. 背包类问题
  4. 路径/图论问题
  5. 构造类问题

1.3 经典问题详解与代码实现

  • 类别一:区间调度问题
  1. 无重叠区间
cpp 复制代码
// LeetCode 435: 无重叠区间
// 问题:移除最少数量的区间,使剩余区间互不重叠
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 = 0;  // 需要移除的区间数
    int end = intervals[0][1];
    
    for (int i = 1; i < intervals.size(); i++) {
        // 如果当前区间开始时间 < 上一个区间的结束时间,说明重叠
        if (intervals[i][0] < end) {
            count++;  // 需要移除当前区间
        } else {
            end = intervals[i][1];  // 不重叠,更新结束时间
        }
    }
    
    return count;
}

/*
示例:
输入: [[1,2],[2,3],[3,4],[1,3]]
排序后: [[1,2],[2,3],[1,3],[3,4]]
贪心选择:[1,2], [2,3], [3,4]
移除:[1,3]
输出: 1
*/
  1. 用最少数量的箭引爆气球
cpp 复制代码
// LeetCode 452: 用最少数量的箭引爆气球
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 end = points[0][1];
    
    for (int i = 1; i < points.size(); i++) {
        // 如果当前气球开始位置 > 上一箭的射击位置
        if (points[i][0] > end) {
            arrows++;        // 需要新的一箭
            end = points[i][1];  // 更新射击位置
        }
        // 否则当前箭可以射爆这个气球,继续
    }
    
    return arrows;
}
  1. 视频拼接
  • 问题的关键在于,所有小于当前右节点的所有左节点中,右节点最长的那个
cpp 复制代码
// LeetCode 1024: 视频拼接
int videoStitching(vector<vector<int>>& clips, int time) {
    // 按开始时间排序,开始时间相同的按结束时间降序
    sort(clips.begin(), clips.end(), 
         [](const vector<int>& a, const vector<int>& b) {
             if (a[0] == b[0]) return a[1] > b[1];
             return a[0] < b[0];
         });
    
    int count = 0;
    int curEnd = 0, nextEnd = 0;
    int i = 0, n = clips.size();
    
    while (i < n && clips[i][0] <= curEnd) {
        // 在当前可覆盖的区间内,找能延伸到最远的
        while (i < n && clips[i][0] <= curEnd) {
            nextEnd = max(nextEnd, clips[i][1]);
            i++;
        }
        
        count++;
        curEnd = nextEnd;
        
        if (curEnd >= time) {
            return count;
        }
    }
    
    return -1;  // 无法覆盖整个区间
}
  • 类别二:分配类问题
  1. 分发饼干
cpp 复制代码
// LeetCode 455: 分发饼干
int findContentChildren(vector<int>& g, vector<int>& s) {
    // g: 孩子的胃口值,s: 饼干的尺寸
    sort(g.begin(), g.end());
    sort(s.begin(), s.end());
    
    int child = 0, cookie = 0;
    while (child < g.size() && cookie < s.size()) {
        if (s[cookie] >= g[child]) {
            child++;  // 当前饼干能满足当前孩子
        }
        cookie++;  // 无论是否满足,饼干都要被考虑
    }
    
    return child;  // 满足的孩子数量
}
相关推荐
好奇龙猫14 小时前
【人工智能学习-AI入试相关题目练习-第七次】
人工智能·学习
D_evil__17 小时前
【Effective Modern C++】第二章 auto:6. 当auto推导的类型不符合要求时,使用显式类型初始化习惯用法
c++
jacGJ17 小时前
记录学习--文件读写
java·前端·学习
哈哈不让取名字17 小时前
基于C++的爬虫框架
开发语言·c++·算法
枷锁—sha18 小时前
【PortSwigger Academy】SQL 注入绕过登录 (Login Bypass)
数据库·sql·学习·安全·网络安全
魔芋红茶19 小时前
Spring Security 学习笔记 2:架构
笔记·学习·spring
剑锋所指,所向披靡!20 小时前
C++之类模版
java·jvm·c++
C+-C资深大佬21 小时前
C++风格的命名转换
开发语言·c++
No0d1es21 小时前
2025年粤港澳青少年信息学创新大赛 C++小学组复赛真题
开发语言·c++
点云SLAM21 小时前
C++内存泄漏检测之手动记录法(Manual Memory Tracking)
开发语言·c++·策略模式·内存泄漏检测·c++实战·new / delete