【贪心算法】(经典实战应用解析(五):单调递增的数字、坏了的计算器、合并区间、⽆重叠区间、⽤最少数量的箭引爆⽓球)


🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》
✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介:

在算法学习中,贪心算法 一直是一个既"简单"又"容易出错"的重要思想.它的核心看似直接:每一步都选择当前最优解,希望最终得到全局最优结果.但真正落到具体题目时,如何判断"当前最优"是否可靠,如何证明贪心策略的正确性,往往才是难点所在.在前几篇内容中,我们已经接触了贪心算法的基本思想和一些典型应用.本篇将继续围绕经典实战题目展开,重点解析五类非常具有代表性的题型:单调递增的数字、坏了的计算器、合并区间、无重叠区间、用最少数量的箭引爆气球.这些题目虽然场景不同,但背后都隐藏着贪心选择的关键逻辑:有的需要从数字结构中寻找调整规律,有的需要反向思考减少操作次数,有的则需要在区间问题中抓住排序与边界选择的本质.通过本文的学习,你将进一步理解贪心算法在不同问题中的应用方式,掌握常见区间类问题的处理套路,并学会如何从题目条件中提炼出合理的贪心策略.希望读完本篇后,你不仅能写出正确代码,更能明白为什么这样的贪心选择是有效的.废话不多说,下面跟着小编的节奏🎵一起去疯狂学习吧!

目录

1.单调递增的数字(OJ题)


解法(贪心):

a. 为了方便处理数中的每一位数字,可以先将整数转换成字符串;

b. 从左往右扫描,找到第一个递减的位置;

c. 从这个位置向前推,推到相同区域的最左端;

d. 该点的值 -1,后面的所有数统一变成 9 .




核心代码

cpp 复制代码
class Solution
{
public:
    //核心函数:输入整数n,返回结果
    int monotoneIncreasingDigits(int n)
    {
        //将数字转换为字符串,方便逐位处理每一位数字
        string s = to_string(n); 
        
        //定义索引i:用于遍历字符串,m为数字的位数
        int i = 0, m = s.size();
        
        //第一步:从左到右遍历,找到第一个【不满足递增】的位置
        //循环条件:未遍历到末尾,且当前位 <= 下一位(满足递增)
        while(i + 1 < m && s[i] <= s[i + 1]) {
            i++; //满足递增就继续向后找
        }

        //特殊情况:遍历完所有位都满足递增,直接返回原数字n
        if(i + 1 == m) {
            return n; 
        }

        //第二步:回推处理【连续相同数字】的情况
        //例:332 → 第一个递减位是第二个3,回推到第一个3,保证结果正确
        while(i - 1 >= 0 && s[i] == s[i - 1]) {
            i--; //向左回退,直到相邻数字不相等
        }

        //第三步:核心操作
        //将当前位置的数字减1,破坏递减关系
        s[i]--;
        //减1之后,当前位置**后面的所有数字都设为9**,保证数字最大
        for(int j = i + 1; j < m; j++) {
            s[j] = '9';
        }

        //将处理后的字符串转回整数,返回最终结果
        return stoi(s);
    }
};

完整测试代码

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

class Solution
{
public:
    // 核心函数:输入整数 n,返回结果
    int monotoneIncreasingDigits(int n)
    {
        // 将数字转换为字符串,方便逐位处理每一位数字
        string s = to_string(n);

        // 定义索引 i:用于遍历字符串,m 为数字的位数
        int i = 0, m = s.size();

        // 第一步:从左到右遍历,找到第一个不满足递增的位置
        while (i + 1 < m && s[i] <= s[i + 1])
        {
            i++;
        }

        // 特殊情况:遍历完所有位都满足递增,直接返回原数字 n
        if (i + 1 == m)
        {
            return n;
        }

        // 第二步:回推处理连续相同数字的情况
        // 例:332 -> 299
        while (i - 1 >= 0 && s[i] == s[i - 1])
        {
            i--;
        }

        // 第三步:当前位置数字减 1
        s[i]--;

        // 当前位置后面的所有数字都设为 9,保证结果最大
        for (int j = i + 1; j < m; j++)
        {
            s[j] = '9';
        }

        // 将处理后的字符串转回整数
        return stoi(s);
    }
};

int main()
{
    Solution sol;

    int testCases[] = {
            10,          // 结果:9
            1234,        // 本身单调递增,结果:1234
            332,         // 结果:299
            120,         // 结果:119
            987,         // 结果:899
            1110,        // 连续相同数字回退,结果:999
            1000,        // 结果:999
            0,           // 结果:0
            9,           // 结果:9
            11,          // 结果:11
            101,         // 结果:99
            110,         // 结果:99
            1234321,     // 结果:1233999
            123454321,   // 结果:123449999
            999999999,   // 本身单调递增,结果:999999999
            2147483647   // int 最大值附近测试,结果:1999999999
    };

    int len = sizeof(testCases) / sizeof(testCases[0]);

    for (int i = 0; i < len; i++)
    {
        int n = testCases[i];

        cout << "测试用例 " << i + 1 << ":" << endl;
        cout << "输入 n:" << n << endl;

        cout << "小于等于 n 的最大单调递增数字:";
        cout << sol.monotoneIncreasingDigits(n) << endl;

        cout << "------------------------" << endl;
    }

    return 0;
}

2.坏了的计算器(OJ题)


解法(贪心):

贪心策略:正难则反

当反着来思考的时候,我们发现:

i. 当 end <= begin 的时候,只能执行加法操作;

ii. 当 end > begin 的时候,对于奇数来说,只能执行加法操作;对于偶数来说,最好的方式就是执行除法操作,这样的话,每次的操作都是固定唯一的.





核心代码

cpp 复制代码
class Solution
{
public:
    int brokenCalc(int startValue, int target)
    {
        //核心思想:正难则反 + 贪心
        //正向推导会有分支(乘2/减1),无法直接贪心;反向推导(除2/加1)路径唯一,最优解
        int ret = 0; // 记录操作的总次数

        //循环:当目标值 > 起始值时,持续反向操作
        while(target > startValue)
        {
            //反向操作1:如果target是偶数,最优选择是除以2(对应正向的乘2操作)
            if(target % 2 == 0) 
                target /= 2;
            //反向操作2:如果target是奇数,只能先加1变成偶数(对应正向的减1操作)
            else 
                target += 1;
            
            ret++; //每执行一次反向操作,次数+1
        }

        //当 target <= startValue 时:
        //只能通过【减1】操作从 startValue 到 target,需要的次数为 startValue - target
        //总次数 = 反向操作次数 + 剩余减1的次数
        return ret + startValue - target;
    }
};

完整测试代码

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

class Solution
{
public:
    int brokenCalc(int startValue, int target)
    {
        // 核心思想:正难则反 + 贪心
        // 正向推导会有分支:乘2 / 减1
        // 反向推导更清晰:除2 / 加1
        int ret = 0;

        // 当 target > startValue 时,持续反向操作
        while (target > startValue)
        {
            // 如果 target 是偶数,反向最优操作是除以 2
            if (target % 2 == 0)
                target /= 2;

                // 如果 target 是奇数,只能先加 1 变成偶数
            else
                target += 1;

            ret++;
        }

        // 当 target <= startValue 时,只能正向通过减 1 到达 target
        return ret + startValue - target;
    }
};

int main()
{
    Solution sol;

    vector<pair<int, int>> testCases = {
            {2, 3},          // 2 -> 4 -> 3,结果 2
            {5, 8},          // 5 -> 4 -> 8,结果 2
            {3, 10},         // 3 -> 6 -> 5 -> 10,结果 3
            {10, 1},         // 只能不断减 1,结果 9
            {1, 1},          // 已经相等,结果 0
            {1, 2},          // 1 -> 2,结果 1
            {1, 3},          // 1 -> 2 -> 4 -> 3,结果 3
            {4, 16},         // 4 -> 8 -> 16,结果 2
            {7, 31},         // 7 -> 8 -> 16 -> 32 -> 31,结果 4
            {10, 100},       // 多次反向除2和加1
            {1024, 1},       // target 小于 startValue
            {1, 1000000000}  // 大数据测试
    };

    for (int i = 0; i < testCases.size(); i++)
    {
        int startValue = testCases[i].first;
        int target = testCases[i].second;

        cout << "测试用例 " << i + 1 << ":" << endl;
        cout << "startValue = " << startValue << ", target = " << target << endl;

        cout << "最少操作次数:";
        cout << sol.brokenCalc(startValue, target) << endl;

        cout << "------------------------" << endl;
    }

    return 0;
}

3.合并区间(OJ题)


解法(排序 + 贪心):

贪心策略:

a. 先按照区间的左端点排序:此时我们会发现,能够合并的区间都是连续的;

b. 然后从左往后,按照求并集的方式,合并区间.

如何求并集:

由于区间已经按照左端点排过序了,因此当两个区间合并的时候,合并后的区间:

a. 左端点就是前一个区间的左端点;

b. 右端点就是两者右端点的最大值.





核心代码

cpp 复制代码
class Solution
{
public:
    //输入:二维数组 intervals 表示若干个区间
    //输出:合并后的不重叠区间数组
    vector<vector<int>> merge(vector<vector<int>>& intervals)
    {
        //第一步:核心预处理
        //按照区间的左端点从小到大排序,这是合并重叠区间的前提
        sort(intervals.begin(), intervals.end());

        //第二步:初始化合并参数
        //用 left、right 记录当前正在合并的区间的左右边界
        int left = intervals[0][0], right = intervals[0][1];
        vector<vector<int>> ret; //存储最终合并后的结果

        //第三步:遍历所有区间,逐个合并
        for(int i = 1; i < intervals.size(); i++)
        {
            //取出当前遍历到的区间的左右端点
            int a = intervals[i][0], b = intervals[i][1];
            
            //情况1:当前区间与正在合并的区间有重叠/相邻
            if(a <= right) 
            {
                //合并区间:更新右边界为两者的最大值
                right = max(right, b);
            }
            //情况2:两个区间完全不重叠
            else 
            {
                //将上一个合并完成的区间存入结果
                ret.push_back({left, right}); 
                //更新左右边界为当前区间,开始新的合并
                left = a;
                right = b;
            }
        }

        //第四步:收尾处理
        //循环结束后,最后一个合并好的区间还没加入结果,必须补充
        ret.push_back({left, right});
        
        //返回最终合并完成的区间列表
        return ret;
    }
};

完整测试代码

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

class Solution
{
public:
    // 输入:二维数组 intervals 表示若干个区间
    // 输出:合并后的不重叠区间数组
    vector<vector<int>> merge(vector<vector<int>>& intervals)
    {
        // 边界情况:空数组直接返回空结果
        if (intervals.empty())
            return {};

        // 第一步:按照区间的左端点从小到大排序
        sort(intervals.begin(), intervals.end());

        // 第二步:初始化合并参数
        int left = intervals[0][0], right = intervals[0][1];
        vector<vector<int>> ret;

        // 第三步:遍历所有区间,逐个合并
        for (int i = 1; i < intervals.size(); i++)
        {
            int a = intervals[i][0], b = intervals[i][1];

            // 当前区间与正在合并的区间有重叠
            if (a <= right)
            {
                right = max(right, b);
            }
                // 当前区间与正在合并的区间无重叠
            else
            {
                ret.push_back({left, right});

                left = a;
                right = b;
            }
        }

        // 第四步:补充最后一个合并好的区间
        ret.push_back({left, right});

        return ret;
    }
};

void printIntervals(const vector<vector<int>>& intervals)
{
    cout << "[";

    for (size_t i = 0; i < intervals.size(); i++)
    {
        cout << "[" << intervals[i][0] << ", " << intervals[i][1] << "]";

        if (i != intervals.size() - 1)
            cout << ", ";
    }

    cout << "]";
}

int main()
{
    Solution sol;

    vector<vector<vector<int>>> testCases = {
            {{1, 3}, {2, 6}, {8, 10}, {15, 18}},   // 普通重叠
            {{1, 4}, {4, 5}},                      // 边界相接,也需要合并
            {{1, 4}, {0, 4}},                      // 包含关系
            {{1, 4}, {2, 3}},                      // 一个区间完全包含另一个区间
            {{1, 2}, {3, 4}, {5, 6}},              // 完全不重叠
            {{1, 10}, {2, 3}, {4, 5}, {6, 7}},     // 大区间包含多个小区间
            {{5, 6}, {1, 3}, {2, 4}, {8, 10}},     // 无序输入
            {{1, 1}},                              // 单个区间
            {},                                    // 空数组
            {{1, 3}, {2, 6}, {6, 8}, {9, 10}},     // 连续边界重叠
            {{-10, -1}, {-5, 0}, {1, 3}},          // 包含负数区间
            {{1, 4}, {0, 0}},                      // 排序后不重叠
            {{1, 5}, {2, 6}, {7, 9}, {8, 10}}      // 多组合并
    };

    for (int i = 0; i < testCases.size(); i++)
    {
        vector<vector<int>> intervals = testCases[i];

        cout << "测试用例 " << i + 1 << ":" << endl;

        cout << "原始区间:";
        printIntervals(intervals);
        cout << endl;

        vector<vector<int>> result = sol.merge(intervals);

        cout << "合并后区间:";
        printIntervals(result);
        cout << endl;

        cout << "------------------------" << endl;
    }

    return 0;
}

4.无重叠区间(OJ题)


解法(贪心):

贪心策略:

a. 按照左端点排序;

b. 当两个区间重叠的时候,为了能够在移除某个区间后,保留更多的区间,我们应该把区间范围较大的区间移除.

如何移除区间范围较大的区间:

由于已经按照左端点排序了,因此两个区间重叠的时候,我们应该移除右端点较大的区间.





核心代码

cpp 复制代码
class Solution
{
public:
    //输入:二维数组 intervals 存储所有区间
    //输出:需要移除的最少区间数量
    int eraseOverlapIntervals(vector<vector<int>>& intervals)
    {
        //第一步:排序
        //按照区间的左端点从小到大排序,为贪心选择做准备
        sort(intervals.begin(), intervals.end());

        //第二步:初始化变量
        int ret = 0; //记录需要移除的区间数量
        //记录当前保留的最后一个区间的左右边界
        int left = intervals[0][0], right = intervals[0][1];

        //第三步:遍历所有区间,判断重叠并处理
        for(int i = 1; i < intervals.size(); i++)
        {
            //取出当前遍历到的区间的左右端点
            int a = intervals[i][0], b = intervals[i][1];
            
            //情况1:当前区间与上一个保留的区间**发生重叠**
            if(a < right) 
            {
                ret++; //必须移除其中一个区间,计数+1
                //贪心核心:保留右端点更小的区间,这样能给后面的区间留出更多空间
                right = min(right, b);
            }
            //情况2:两个区间**不重叠**,无需移除
            else 
            {
                //更新当前保留的最后一个区间的右边界
                right = b;
            }
        }

        //返回最终需要移除的最少区间数
        return ret;
    }
};

完整测试代码

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

class Solution
{
public:
    // 输入:二维数组 intervals 存储所有区间
    // 输出:需要移除的最少区间数量
    int eraseOverlapIntervals(vector<vector<int>>& intervals)
    {
        // 边界情况:没有区间,不需要移除
        if (intervals.empty())
            return 0;

        // 第一步:排序
        // 按照区间的左端点从小到大排序,为贪心选择做准备
        sort(intervals.begin(), intervals.end());

        // 第二步:初始化变量
        int ret = 0;

        // 记录当前保留的最后一个区间的左右边界
        int left = intervals[0][0], right = intervals[0][1];

        // 第三步:遍历所有区间,判断重叠并处理
        for (int i = 1; i < intervals.size(); i++)
        {
            // 取出当前遍历到的区间的左右端点
            int a = intervals[i][0], b = intervals[i][1];

            // 情况1:当前区间与上一个保留的区间发生重叠
            if (a < right)
            {
                ret++;

                // 贪心核心:保留右端点更小的区间
                right = min(right, b);
            }
                // 情况2:两个区间不重叠
            else
            {
                left = a;
                right = b;
            }
        }

        return ret;
    }
};

void printIntervals(const vector<vector<int>>& intervals)
{
    cout << "[";

    for (size_t i = 0; i < intervals.size(); i++)
    {
        cout << "[" << intervals[i][0] << ", " << intervals[i][1] << "]";

        if (i != intervals.size() - 1)
            cout << ", ";
    }

    cout << "]";
}

int main()
{
    Solution sol;

    vector<vector<vector<int>>> testCases = {
            {{1, 2}, {2, 3}, {3, 4}, {1, 3}},       // 移除 [1,3],结果 1
            {{1, 2}, {1, 2}, {1, 2}},               // 保留一个,移除 2 个
            {{1, 2}, {2, 3}},                       // 不重叠,结果 0
            {{1, 100}, {11, 22}, {1, 11}, {2, 12}}, // 结果 2
            {{1, 4}, {2, 3}, {3, 5}},               // 保留 [2,3] 和 [3,5],移除 1 个
            {{1, 3}, {2, 4}, {3, 5}, {6, 8}},       // 移除 1 个
            {{1, 5}, {2, 6}, {3, 7}, {4, 8}},       // 全部重叠,移除 3 个
            {{1, 2}, {3, 4}, {5, 6}},               // 完全不重叠,结果 0
            {{-10, -1}, {-5, 0}, {1, 3}},           // 包含负数区间,结果 1
            {{1, 2}},                               // 单个区间,结果 0
            {},                                     // 空数组,结果 0
            {{2, 3}, {1, 2}, {3, 4}, {1, 3}},       // 无序输入,排序后处理
            {{1, 2}, {2, 2}, {2, 3}},               // 边界相接不算重叠,结果 0
            {{1, 3}, {1, 2}, {2, 3}, {3, 4}}        // 保留右端点更小的区间,结果 1
    };

    for (int i = 0; i < testCases.size(); i++)
    {
        vector<vector<int>> intervals = testCases[i];

        cout << "测试用例 " << i + 1 << ":" << endl;

        cout << "原始区间:";
        printIntervals(intervals);
        cout << endl;

        cout << "需要移除的最少区间数量:";
        cout << sol.eraseOverlapIntervals(intervals) << endl;

        cout << "------------------------" << endl;
    }

    return 0;
}

5.⽤最少数量的箭引爆⽓球(OJ题)


解法(贪心):

贪心策略:

a. 按照左端点排序,我们发现,排序后有这样一个性质:互相重叠的区间都是连续的;

b. 这样,我们在射箭的时候,要发挥每一支箭最大的作用,应该把互相重叠的区间统一引爆.

如何求互相重叠区间?

由于我们是按照左端点排序的,因此对于两个区间,我们求的是它们的交集:

a. 左端点为两个区间左端点的最大值(但是左端点不会影响我们的合并结果,所以可以忽略);

b. 右端点为两个区间右端点的最小值.





核心代码

cpp 复制代码
class Solution
{
public:
    //输入:二维数组 points 存储所有气球的区间
    //输出:引爆所有气球所需的最少弓箭数量
    int findMinArrowShots(vector<vector<int>>& points)
    {
        //第一步:排序
        //按照气球区间的左端点从小到大排序,方便贪心遍历
        sort(points.begin(), points.end());

        //第二步:初始化变量
        int right = points[0][1];  //记录当前箭能覆盖的最右侧边界(重叠区间的最小右边界)
        int ret = 1;                //最少需要1支箭(至少有一个气球)

        //第三步:遍历所有气球,贪心选择射箭位置
        for(int i = 1; i < points.size(); i++)
        {
            //取出当前气球的左右端点
            int a = points[i][0], b = points[i][1];
            
            //情况1:当前气球与上一支箭的覆盖区间重叠
            //可以用同一支箭引爆,更新右边界为最小值(保证所有重叠气球都能被击穿)
            if(a <= right) 
            {
                right = min(right, b);
            }
            //情况2:当前气球与上一支箭的覆盖区间不重叠
            //需要新增一支箭,更新右边界为当前气球的右边界
            else 
            {
                ret++;
                right = b;
            }
        }

        //返回最少需要的箭数
        return ret;
    }
};

完整测试代码

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

class Solution
{
public:
    // 输入:二维数组 points 存储所有气球的区间
    // 输出:引爆所有气球所需的最少弓箭数量
    int findMinArrowShots(vector<vector<int>>& points)
    {
        // 边界情况:没有气球,不需要箭
        if (points.empty())
            return 0;

        // 第一步:排序
        // 按照气球区间的左端点从小到大排序,方便贪心遍历
        sort(points.begin(), points.end());

        // 第二步:初始化变量
        int right = points[0][1];  // 当前箭能覆盖的最右侧边界
        int ret = 1;               // 至少有一个气球时,最少需要 1 支箭

        // 第三步:遍历所有气球,贪心选择射箭位置
        for (int i = 1; i < points.size(); i++)
        {
            int a = points[i][0], b = points[i][1];

            // 情况1:当前气球与上一支箭的覆盖区间重叠
            if (a <= right)
            {
                // 更新重叠区间的最小右边界
                right = min(right, b);
            }
                // 情况2:当前气球与上一支箭的覆盖区间不重叠
            else
            {
                ret++;
                right = b;
            }
        }

        return ret;
    }
};

void printPoints(const vector<vector<int>>& points)
{
    cout << "[";

    for (size_t i = 0; i < points.size(); i++)
    {
        cout << "[" << points[i][0] << ", " << points[i][1] << "]";

        if (i != points.size() - 1)
            cout << ", ";
    }

    cout << "]";
}

int main()
{
    Solution sol;

    vector<vector<vector<int>>> testCases = {
            {{10, 16}, {2, 8}, {1, 6}, {7, 12}},       // 经典示例,结果 2
            {{1, 2}, {3, 4}, {5, 6}, {7, 8}},          // 完全不重叠,结果 4
            {{1, 2}, {2, 3}, {3, 4}, {4, 5}},          // 边界相接算重叠,结果 2
            {{1, 10}, {2, 3}, {4, 5}, {6, 7}},         // 大区间覆盖多个小区间,结果 3
            {{1, 10}, {2, 9}, {3, 8}, {4, 7}},         // 全部重叠,结果 1
            {{1, 2}},                                  // 单个气球,结果 1
            {},                                        // 空数组,结果 0
            {{-10, -1}, {-5, 0}, {1, 3}},              // 包含负数区间,结果 2
            {{5, 6}, {1, 2}, {2, 5}, {7, 9}},          // 无序输入,结果 2
            {{1, 5}, {2, 6}, {7, 9}, {8, 10}},         // 两组重叠区间,结果 2
            {{1, 1}, {1, 1}, {1, 1}},                  // 点区间完全相同,结果 1
            {{1, 1}, {2, 2}, {3, 3}},                  // 点区间不重叠,结果 3
            {{1, 3}, {3, 3}, {3, 5}},                  // 都能在 3 处射穿,结果 1
            {{-2147483648, 2147483647}, {0, 0}}        // 极值区间,结果 1
    };

    for (int i = 0; i < testCases.size(); i++)
    {
        vector<vector<int>> points = testCases[i];

        cout << "测试用例 " << i + 1 << ":" << endl;

        cout << "气球区间:";
        printPoints(points);
        cout << endl;

        cout << "最少弓箭数量:";
        cout << sol.findMinArrowShots(points) << endl;

        cout << "------------------------" << endl;
    }

    return 0;
}

🚀真正的勇者不是流泪的人,而是含泪奔跑的人!


敬请期待下一篇文章内容的更新【贪心算法】(经典实战应用解析(六):整数替换、俄罗斯套娃信封问题、可被三整除的最⼤和、距离相等的条形码、重构字符串)


每日心灵鸡汤: 心有分寸,行有方向!

遇大事要静,遇难事要变,遇烂事要离,遇顺事要敛.人的一生,就像一趟充满未知的旅程,会撞见突如其来的风雨,会邂逅意料之外的坦途,也会碰上纠缠不休的纷扰.我们无法左右事情的走向,却能调整自己面对世事的心态.遇事慌乱,只会乱了方寸,让局面愈发棘手;沉下心来,稳住阵脚,才能在迷雾中找到破局的关键.前路受阻,一味硬抗只会耗尽心力;懂得变通,换个思路,往往能遇到柳暗花明的转机.先改变看待事物的角度,再提升人生的高度.以静制动,以变求通,以离止损,以敛养慧.守好这四颗心,无论前路是晴是雨,都能走得从容,活得坦荡.

相关推荐
Brilliantwxx9 小时前
【C++】深度剖析 · 继承 (虚基表+虚函数表)
开发语言·c++
一只旭宝9 小时前
【C加加入门精讲15】:IO流缓冲区、字符串流、缓冲流及STL vector容器零基础实战教程一、博客前言
开发语言·c++
alwaysrun9 小时前
C++之高性能跨平台日志库spdlog
c++·后端·编程语言
我不是懒洋洋9 小时前
手写数字识别:从零实现一个卷积神经网络(CNN)
c++
人道领域9 小时前
【LeetCode刷题日记】106.从遍历序列重建二叉树:手撕递归边界,彻底搞懂左闭右闭 vs 左闭右开
java·算法·leetcode
念恒123069 小时前
Python(while循环)
数据结构·python·算法
bqq198610269 小时前
Redis持久化
数据结构·数据库·redis·缓存
BestOrNothing_20159 小时前
C++零基础到工程实战(5.1):初识函数—定义调用、参数返回值、栈区内存与变量作用域分析
c++·生命周期·作用域·变量·函数·栈内存
运筹vivo@9 小时前
LeetCode 2540. 最小公共值
算法·leetcode·职场和发展