栈 stack:从零搭起一套「后进先出」的工具箱
栈(stack)是一种「后进先出」的数据结构——想象一摞只能从顶部放、取的书,最后放上去的最先被拿走。这一篇把它的 API、四道经典栈题、以及延伸出来的「表达式」相关概念串成一份工具笔记。 一、五分钟上手:std::stack栈在 C++ 标准库里只对外暴露 5 个常用接口: 方法 作用 是否改动栈 push(x) 把 x 压到栈顶 ✅ pop() 弹出栈顶(不返回值) ✅ top() 返回栈顶元素的引用 ❌ empty() 栈是否为空 ❌ size() 元素个数 ❌ 最容易踩的坑:pop() 不返回值。要拿到栈顶值,必须先 top() 再 pop()。 1234567891011121314151617181920#include <iostream>#include <stack>int main() { std::stack<int> s; s.push(1); s.push(2); s.push(3); std::cout << "To...
蓝桥杯 2026 萌新首秀第二场 T4 校园打印
题目链接:「萌新首秀」千校新生编程排位赛·第二场 一句话概括解法:每张打印卡只有「买 / 不买」两种选择,M ≤ 18 → 枚举 2^M 种组合,取花费最小。这道题是二进制枚举(位掩码枚举)的模板题。 一、题目复述小蓝要打印 Q 页资料,打印店有两种方式: 直接打印:每页 P 元; 打印卡:共有 M 种卡,第 i 种需要花 Cᵢ 元购买,买后最多可打印 Lᵢ 页。每种卡最多购买一次,可以同时买多种。没用的页数不退钱。 求打印完全部 Q 页资料,最少要花多少钱。 数据范围: 变量 范围 提示 Q, P 1 ~ 10⁹ Q × P 最大 10¹⁸,必须用 long long M 1 ~ 18 2¹⁸ = 262144,这是本题的突破口 Lᵢ, Cᵢ 1 ~ 10⁹ 累加会超过 int 二、把问题抽象成「子集选择」先写出花费公式。假设我们决定买下一个卡片集合 S: 12345总页数 = Σ Lᵢ (i ∈ S)总卡费 = Σ Cᵢ (i ∈ S)如果 总页数 ≥ Q:花费 = 总卡费如果 总页数 < Q:花费 = 总卡费 + (Q - 总页...
蓝桥杯 2026 萌新首秀第二场 T3 图书归位(书架调整)
题目链接:「萌新首秀」千校新生编程排位赛·第二场 一句话概括解法:先用「奇偶性不变量」判断有无解,再从左到右贪心把每个数归位。这道题的核心考点不是敲代码,而是证明不清不变量就写不对判定条件。 一、题目复述N 本书编号 1 ~ N 排在书架上,顺序可能是乱的。目标状态是 1, 2, 3, ..., N(第 i 个位置放编号为 i 的书)。 一次操作:任选连续的三个位置 x, x+1, x+2,交换 a[x] 与 a[x+2],中间的 a[x+1] 保持不动。 例如 1 2 3 4 5,选第 2 ~ 4 个位置,交换 2 和 4,得到 1 4 3 2 5。 求把书架恢复成 1, 2, ..., N 的最少操作次数;无论如何都无法完成时输出 -1。 数据范围:3 ≤ N ≤ 2000,输入保证是 1 ~ N 的一个排列。 二、第一步:把操作翻译成人话「选连续三个位置、交换两端」听起来复杂,但作用到元素身上只有一句话: 一次操作 = 某个元素向左或向右跳 2 格,代价 1。 因为中间那个位置的书自始至终没被动过,我们完全可以忽略它,只看两端。 于是问题变成:在一个数组里,每次可以把一...
Git 常用命令与工作流
本文记录 Git 的核心模型和日常最常用的命令,配合分支用法和「改坏了怎么救」的排查表,足够覆盖个人博客与日常开发。 一、四个区域:git 的全核心理解 git 的关键,是搞清楚四个区域之间的关系: graph LR A["工作区<br/>你编辑的文件"] -->|git add| B["暂存区<br/>待提交清单"] B -->|git commit| C["本地仓库<br/>提交历史"] C -->|git push| D["远程仓库<br/>GitHub"] D -->|git pull| C 区域 是什么 由谁管理 工作区 你实际编辑的文件 你的编辑器 暂存区 「这次要提交哪些改动」的清单 git add 本地仓库 一次次提交快照组成的历史,全在你电脑上 git commit 远程仓库 GitHub 上的那份,部署平台从这里拉代码 ...
C++ 友元(friend)详解
友元(friend)是 C++ 提供的一种「定向开权限」机制:允许类外部的函数或另一个类访问本类的 private / protected 成员。本文介绍友元的三种形式、为什么需要它,以及使用时的注意事项。 一、友元是什么友元函数不是类的成员函数,却可以访问类的 private / protected 成员。 三个关键认知: 不是成员函数:它属于全局作用域(或另一个类),不在本类作用域内; 没有 this 指针:因为不属于任何对象,想访问某个对象的成员就必须把它作为参数传进来; 权限是「授予」的:由被访问的类自己用 friend 声明,主动权在被访问者手里,而不是想访问的人自己宣布。 二、友元函数语法是类内声明 + 类外定义:在类里加 friend 声明,函数体写在类外面。 123456789101112131415161718192021#include <iostream>using namespace std;class A {private: int num;public: A(int x) : num(x) {} frien...
C++ 异常处理入门(try / catch / throw)
程序运行中会出现各种「特殊情况」,导致后续逻辑无法正常执行:文件打不开、内存不足、网络超时、除数为零……这类情况统称为异常。本文介绍 C++ 异常处理的三个关键字、catch 的匹配顺序,以及重新抛出的用法。 一、三个关键字 关键字 作用 try 包裹一段「可能出问题」的代码,检查其中产生的异常 catch 捕获并处理 try 中产生的异常 throw 在 try 块或它调用的函数里,抛出异常 配合使用的基本形态: 12345try { // 可能抛异常的代码} catch (异常类型& e) { // 处理这个异常} try 块里只要有一处 throw 被执行,后面的语句就不再执行,控制流立刻跳到匹配的 catch。 二、第一个例子:除零异常123456789101112131415161718#include <iostream>#include <stdexcept> // runtime_error 所需头文件using namespace std;float div...
C++ 类与面向对象核心知识整理
本文把 C++ 类相关的核心知识点串成一条线:继承 → 函数重写 → 虚函数 → 多态 → 抽象类,再补上 static、const、this、友元等常用特性。 一、头文件保护:#pragma once写多文件程序时,同一个头文件可能被重复 #include(比如 A 和 B 都包含了 shape.h),会导致类被重复定义的编译错误。#pragma once 放在头文件最开头,保证它在一个编译单元里只被展开一次: 123456// shape.h#pragma onceclass Shape { // ...}; 它与老式的 include guard 等价,但更简洁、不易写错: 12345// 老式写法,效果相同#ifndef SHAPE_H#define SHAPE_Hclass Shape { /* ... */ };#endif #pragma once 是编译器扩展(主流编译器都支持),写头文件时优先用它。 二、继承2.1 继承关系示意 graph TD A[Shape] --> B[Circle] A --&g...
C++ 宏定义(#define)详解
宏(macro)是 C/C++ 预处理器提供的文本替换机制,在正式编译之前由预处理器完成替换。本文介绍对象式宏、条件编译、宏函数、# 与 ## 运算符,以及预定义宏,并说明现代 C++ 中宏的替代方案。 一、宏的本质宏不是变量也不是函数,而是编译前的文本替换。比如: 1#define NUM 123 预处理阶段,代码里所有的 NUM 都会被原样替换成 123,然后才进入编译。理解这一点是看懂宏所有「坑」的前提。 二、对象式宏(不带参数)12#define NUM 123 // 定义#undef NUM // 取消定义,之后 NUM 恢复为未定义状态 宏名通常全大写,和普通变量区分(约定俗成,非语法要求); 定义行末尾不要加分号——分号会被一起替换进去,多数场景下多余甚至出错; 宏没有类型,不做类型检查,只是纯文本。 现代 C++ 中,常量优先用 const / constexpr: 1constexpr int NUM = 123; // 有类型、有作用域、编译器会检查,更安全 三、条件编译条件编译指令根据「宏是否被定义」决定某段代码...
C++ 类模板(Class Template)入门
类模板是 C++ 泛型编程的核心。上一篇讲了函数模板,本文接着讲类模板:如何定义、成员函数怎么写、全特化与部分特化、非类型参数、默认参数,以及模板模板参数。 一、为什么需要类模板和函数模板同理:如果为 int、float、double 各写一个 Vector 类,逻辑完全相同,只是元素类型不同。类模板让我们写一份代码,编译器根据实例化时的类型自动生成对应版本。 二、基本定义语法123456789101112template <typename T>class Vec3 {private: T m_v[3];public: Vec3(T v1, T v2, T v3) { m_v[0] = v1; m_v[1] = v2; m_v[2] = v3; } T get(int i) const { return m_v[i]; }}; 使用时用 类名<具体类型> 实例化: 123456789#include <iostream>...
C++ 函数模板(Function Template)入门
函数模板是 C++ 泛型编程的基础。本文从「为什么需要模板」讲起,依次介绍模板的定义、实例化、多类型参数、重载与特化,最后整理几个初学者常踩的坑。 一、为什么需要函数模板假设要写一个求两个数较大值的函数,不用模板就得为每种类型重载一遍: 1234int max(int a, int b) { return a > b ? a : b; }double max(double a, double b) { return a > b ? a : b; }char max(char a, char b) { return a > b ? a : b; }// 每多一种类型就要再写一遍,逻辑完全相同…… 这些函数的逻辑一模一样,只是参数类型不同。函数模板让我们只写一份代码,由编译器根据调用时的类型自动生成对应版本。 二、定义语法12template <class T> // class 是 C++ 关键字,在这里表示「任意类型」template <typename T...