跳转至

C++ 与 LeetCode 知识点

  1. 语言基础
  2. STL 容器与常用工具
  3. 常见数据结构
  4. 算法思想

1. 语言基础

1.1 基本类型 int / double / char / bool

是什么

  • int:整数,比如 1100
  • double:小数,比如 3.14
  • char:单个字符,比如 'A'
  • bool:布尔值,只有 truefalse

怎么用

int num = 10;
double pi = 3.14;
char ch = 'A';
bool ok = true;

应用场景

  • int:数组下标、计数、答案
  • double:平均值、几何题、精度计算
  • char:字符串中的单个字符
  • bool:条件判断、标记某件事是否成立

易错点

  • char 用单引号,string 用双引号
  • bool 输出时默认常显示成 10
  • int / int 还是整数除法,比如 3 / 2 = 1
  • 大数据范围时,int 可能不够,要考虑 long long

记忆方法

  • int 看成“整”
  • double 看成“双精度小数”
  • char 看成“一个字符”
  • bool 看成“真/假开关”

1.2 const 常量

是什么

  • const 表示这个值定义后不允许修改

怎么用

const int days = 7;

应用场景

  • 表示固定配置
  • 保护函数参数不被修改
  • 提高代码可读性和安全性

易错点

  • const 变量定义后不能再赋值
  • const vector<int>& nums 里的 const 是“不能通过这个引用改内容”
  • 不要把“不能改引用指向”和“不能改引用内容”混在一起

记忆方法

  • const = constant = 常量 = 只读

1.3 引用 &

是什么

  • 引用就是变量的“别名”
  • 改引用,本质上就是改原变量

怎么用

int a = 5;
int& ref = a;
ref = 20;

应用场景

  • 函数传参时避免拷贝大对象
  • for 循环里直接修改容器元素
  • 给已有变量起另一个名字

易错点

  • 引用必须在定义时绑定,后面不能改绑到别的变量
  • int& ref = a; 不是“复制”,而是“共享同一个值”
  • const int&int& 不一样,前者不能改值

记忆方法

  • 引用不是新盒子,而是给旧盒子贴了个新标签

1.4 指针 *

是什么

  • 指针里存的是地址
  • *ptr 才是这个地址指向的值

怎么用

int score = 95;
int* ptr = &score;
cout << *ptr << endl;

应用场景

  • 链表
  • 二叉树
  • 动态内存
  • 函数中修改外部变量

易错点

  • int* ptr 是定义指针,*ptr 是解引用,不是一回事
  • 空指针 nullptr 不能直接解引用
  • &x 是取地址,*ptr 是取地址里的值,方向别搞反

记忆方法

  • & 是“拿到门牌号”
  • * 是“顺着门牌号找到屋里的人”

1.5 默认参数

是什么

  • 调用函数时如果少传参数,就用函数定义里给的默认值

怎么用

int add(int a, int b = 10)
{
    return a + b;
}

应用场景

  • 某个参数经常是固定值
  • 想让函数调用更简洁

易错点

  • 默认参数通常写在函数声明或定义里,只写一处
  • 有默认值的参数一般放后面
  • 调用时如果传了值,就不会再用默认值

记忆方法

  • “你不说,我就按默认方案办”

1.6 值传递 / 引用传递 / 指针传递

是什么

  • 值传递:传副本
  • 引用传递:传原对象别名
  • 指针传递:传地址

怎么用

void changeByValue(int x) { x = 100; }
void changeByReference(int& x) { x = 100; }
void changeByPointer(int* x) { *x = 100; }

应用场景

  • 判断函数会不会改外部变量
  • 优化参数传递效率
  • 刷题时修改链表、树节点

易错点

  • 值传递里改了参数,外部不变
  • 指针传递前要先判断是不是 nullptr
  • 大对象如果值传递,会多一次拷贝

记忆方法

  • 值传递:复印件
  • 引用传递:同一个人
  • 指针传递:给你地址你自己去找

1.7 template 模板

是什么

  • 让同一份代码适配多种类型

怎么用

template <typename T>
T myMax(T a, T b)
{
    return a > b ? a : b;
}

应用场景

  • 写通用函数
  • 写通用类
  • 理解 STL 为什么能支持很多类型

易错点

  • 模板参数类型要能支持你写的操作,比如这里要求能比较 >
  • 新手阶段常把模板想复杂,本质上就是“先不写死类型”
  • 模板报错通常会比较长,要学会先看自己传入的类型是否匹配

记忆方法

  • 模板就是“类型占位符”

1.8 struct

是什么

  • 把几个相关变量打包成一个整体

怎么用

struct Student
{
    string name;
    int score;
};

应用场景

  • 学生信息
  • 比赛记录
  • 链表节点
  • 树节点

易错点

  • struct 里也可以有函数
  • struct 默认成员权限是 public
  • 不要把“结构体”和“类”看成完全不同的东西,它们很像

记忆方法

  • struct = 打包数据

1.9 class

是什么

  • 把“数据”和“操作数据的函数”封装在一起

怎么用

class Person
{
private:
    string name;
    int age;

public:
    Person(string n, int a) : name(n), age(a) {}
};

应用场景

  • 系统学习 C++ 面向对象
  • 需要封装属性和行为
  • 项目开发中组织复杂逻辑

易错点

  • class 默认权限是 private
  • 构造函数不是普通函数,它在对象创建时自动调用
  • private 成员外部不能直接访问

记忆方法

  • class = 更讲封装的 struct

2. STL 容器与常用工具

2.1 vector

是什么

  • 动态数组,长度可以自动变化

怎么用

vector<int> nums;
nums.push_back(5);
nums.push_back(1);

应用场景

  • 存一组数字
  • 做题里最常见的顺序容器
  • 图、前缀和、DP 数组等

易错点

  • nums[i] 访问前要确保没越界
  • vector 不能直接访问 nums[0]
  • size() 返回的是无符号类型,新手做减法时要小心

记忆方法

  • vector = 会自动长大的数组

2.2 sort

是什么

  • STL 里的排序函数

怎么用

sort(nums.begin(), nums.end());

应用场景

  • 排名
  • 二分前预处理
  • 去重前预处理
  • 区间题、贪心题

易错点

  • 排序默认是升序
  • 区间写法是左闭右开,所以是 begin()end()
  • 排序后原顺序会被打乱

记忆方法

  • “先排好,再做题”

2.3 lambda 匿名函数

是什么

  • 临时写在使用位置的小函数

怎么用

sort(nums.begin(), nums.end(),
[](int x, int y)
{
    return x > y;
});

应用场景

  • 自定义排序规则
  • 临时比较逻辑
  • 配合 STL 算法使用

易错点

  • return x > y; 表示降序
  • [] 是捕获列表,新手先记住“这是 lambda 的一部分”
  • 写比较函数时要返回布尔值

记忆方法

  • lambda = “现场写一个小函数”

2.4 string

是什么

  • 字符串类型,本质上是字符序列

怎么用

string s = "hello";
cout << s.size() << endl;
cout << s[0] << endl;

应用场景

  • 子串问题
  • 回文串
  • 模拟题
  • 双指针字符串题

易错点

  • s[0] 是字符,不是字符串
  • 注意越界,空字符串没有 s[0]
  • size() 返回长度,不是最后一个下标

记忆方法

  • string = 字符数组的升级版

2.5 pair

是什么

  • 一次存两个值

怎么用

pair<string, int> studentPair;
studentPair.first = "Bob";
studentPair.second = 95;

应用场景

  • 坐标 (x, y)
  • 键值对
  • 区间左右端点

易错点

  • firstsecond 别写反
  • 两个值的类型可以不同
  • 简单场景很好用,但数据更多时更适合 struct

记忆方法

  • pair = 绑定成一对

2.6 unordered_map

是什么

  • 哈希表,存 key -> value

怎么用

unordered_map<string, int> age;
age["Tom"] = 18;

应用场景

  • 计数
  • 映射
  • 查重
  • 两数之和

易错点

  • age["Tom"] 如果不存在,会创建这个键
  • 只判断是否存在时,更建议用 count
  • unordered_map 遍历顺序不是有序的

记忆方法

  • “拿 key,秒找 value”

2.7 set / unordered_set

是什么

  • set:自动去重,且有序
  • unordered_set:自动去重,查找更快,但无序

怎么用

set<int> s = {3, 1, 2, 2};
unordered_set<int> us = {10, 20, 10};

应用场景

  • 去重
  • 判重
  • 判断某个值是否出现过

易错点

  • set 会自动排序
  • unordered_set 不保证输出顺序
  • 两者都不会存重复元素

记忆方法

  • set = 去重集合
  • unordered_set = 更快的去重集合

2.8 map

是什么

  • 有序的 key -> value

怎么用

map<string, int> scoreMap;
scoreMap["math"] = 90;

应用场景

  • 需要按 key 顺序遍历
  • 统计频次后有序输出

易错点

  • map 一般比 unordered_map 慢一点
  • map 是按 key 排序,不是按 value
  • scoreMap["x"] 不存在时也会新建

记忆方法

  • map = 有序版哈希表思路

2.9 stack

是什么

  • 栈,后进先出

怎么用

stack<int> st;
st.push(10);
st.top();
st.pop();

应用场景

  • 括号匹配
  • 单调栈
  • 模拟函数调用过程

易错点

  • pop() 只弹出,不返回值
  • 取栈顶要用 top()
  • 空栈不能 top()pop()

记忆方法

  • 像一摞盘子,最后放的先拿

2.10 queue

是什么

  • 队列,先进先出

怎么用

queue<int> q;
q.push(1);
q.front();
q.pop();

应用场景

  • BFS
  • 层序遍历
  • 任务调度

易错点

  • front() 取队头,不删除
  • pop() 删除队头,不返回值
  • 空队列不能直接 front()

记忆方法

  • 像排队,先来的先处理

2.11 priority_queue

是什么

  • 优先队列,默认大根堆

怎么用

priority_queue<int> maxHeap;
maxHeap.push(5);
maxHeap.top();

应用场景

  • Top K
  • 每次取当前最大值
  • 堆题

易错点

  • 默认取最大,不是最小
  • pop() 也不返回值
  • 如果要最小堆,需要额外写法

记忆方法

  • “谁优先级高,谁先出来”

3. 常见数据结构

3.1 链表

是什么

  • 每个节点存当前值和下一个节点地址

怎么用

struct ListNode
{
    int val;
    ListNode* next;
};

应用场景

  • 反转链表
  • 删除节点
  • 合并链表
  • 快慢指针

易错点

  • 容易把 next 指向搞丢
  • 改指针前要先保存下一节点
  • 空链表要先判空

记忆方法

  • 链表像“火车车厢”,每节只知道下一节

3.2 二叉树

是什么

  • 每个节点最多有左孩子和右孩子

怎么用

struct TreeNode
{
    int val;
    TreeNode* left;
    TreeNode* right;
};

应用场景

  • 前序、中序、后序遍历
  • 层序遍历
  • 递归搜索
  • 二叉搜索树

易错点

  • 左右孩子容易写反
  • 递归时要注意空节点
  • 树题很多其实是在练递归返回值含义

记忆方法

  • 树就是“会分叉的链表”

3.3 BFS 层序遍历

是什么

  • 一层一层地访问节点,通常配合 queue

怎么用

queue<TreeNode*> q;
q.push(root);

应用场景

  • 二叉树层序遍历
  • 图的最短步数
  • 从起点扩散搜索

易错点

  • 忘了判空根节点
  • 进队和出队顺序要清楚
  • 题目要求“按层处理”时,常要记录当前层大小

记忆方法

  • BFS = “一圈一圈往外扩”

4. 算法思想

4.1 双指针

是什么

  • 用两个指针一起移动,减少重复遍历

怎么用

int left = 0;
int right = nums.size() - 1;
while (left < right)
{
    left++;
    right--;
}

应用场景

  • 有序数组
  • 回文判断
  • 移除元素
  • 快慢指针

易错点

  • 左右指针移动条件容易写错
  • 边界要想清楚是 < 还是 <=
  • 指针什么时候移动,必须和题意绑定

记忆方法

  • “一个人太慢,就两个人一起夹着找”

4.2 二分查找

是什么

  • 在有序数组里每次看中间,砍掉一半区间

怎么用

while (left <= right)
{
    int mid = left + (right - left) / 2;
}

应用场景

  • 查目标值
  • 找左边界/右边界
  • 答案二分

易错点

  • 前提通常是“有序”
  • mid 推荐写成 left + (right - left) / 2
  • 更新边界时很容易死循环
  • 查值和查边界,写法不完全一样

记忆方法

  • “每次砍半”

4.3 滑动窗口

是什么

  • 用一个连续区间在数组或字符串上滑动

怎么用

for (int right = 0; right < nums.size(); right++)
{
    while (条件满足)
    {
        left++;
    }
}

应用场景

  • 最长子串
  • 最短子数组
  • 连续区间统计

易错点

  • 先扩右边还是先缩左边,逻辑要统一
  • 窗口内维护的量要同步更新,比如 sum、计数器
  • 不是所有子数组题都能用滑动窗口,通常要求某种单调性

记忆方法

  • 像拿一个会移动的窗框去框住一段区间

4.4 前缀和

是什么

  • 预处理前面所有元素的累计和

怎么用

prefix[i + 1] = prefix[i] + nums[i];

应用场景

  • 多次区间求和
  • 子数组和
  • 二维矩阵和

易错点

  • 常见写法是 prefix 长度比原数组大 1
  • 区间 [l, r] 的和通常是 prefix[r + 1] - prefix[l]
  • 下标很容易错一位

记忆方法

  • “前面先攒好,后面直接减”

4.5 DFS 递归

是什么

  • 一条路先走到底,再回头换路

怎么用

void dfs(int depth)
{
    if (depth == 0) return;
    dfs(depth - 1);
}

应用场景

  • 树遍历
  • 图遍历
  • 回溯搜索

易错点

  • 一定要有结束条件
  • 容易重复访问,图题常需要 visited
  • 递归层数太深可能爆栈

记忆方法

  • DFS = “一路钻到底”

4.6 回溯

是什么

  • 本质上是“带撤销的 DFS”

怎么用

path.push_back(nums[i]);
backtrack(...);
path.pop_back();

应用场景

  • 子集
  • 排列
  • 组合
  • N 皇后

易错点

  • 忘记撤销选择 pop_back()
  • 结果数组和当前路径数组别混
  • 去重题通常要额外处理重复元素

记忆方法

  • “试一下,不行就退回来”

4.7 贪心

是什么

  • 每一步先做当前看起来最优的选择

怎么用

  • 不一定有固定模板,关键是证明“局部最优能推出全局最优”

应用场景

  • 跳跃游戏
  • 区间覆盖
  • 分发饼干
  • 股票部分题型

易错点

  • 不是“看起来合理”就一定能贪心
  • 贪心题难点通常不是写代码,而是想清楚策略为什么对
  • 局部最优不一定总能推出全局最优

记忆方法

  • “先拿眼前最划算的”

4.8 动态规划

是什么

  • 把大问题拆成小问题,当前答案由之前状态推出来

怎么用

常见思考顺序:

  1. dp[i] 表示什么
  2. 状态转移方程是什么
  3. 初始值是什么
  4. 遍历顺序是什么

应用场景

  • 爬楼梯
  • 背包问题
  • 最长上升子序列
  • 编辑距离

易错点

  • 最容易错的是 dp[i] 的定义不清
  • 初始值不对,后面全错
  • 遍历顺序可能影响结果
  • 别把 DP 和递归硬混,先写清状态

记忆方法

  • “今天的答案,靠昨天和前天推出来”

5. 刷题时的使用建议

5.1 看到题先想什么

  • 是不是数组 / 字符串题
  • 能不能排序
  • 有没有哈希表优化
  • 是不是连续区间,可以考虑滑动窗口或前缀和
  • 是不是树 / 图,考虑 DFS 或 BFS
  • 是不是最值或计数,考虑动态规划

5.2 新手最容易混的几组

  • 引用指针
  • stack::top()pop()
  • queue::front()pop()
  • mapunordered_map
  • setunordered_set
  • DFS回溯
  • 贪心动态规划

5.3 一份推荐学习顺序

  1. 先把 vectorstringunordered_mapsort 学熟
  2. 再学 stackqueuesetmap
  3. 然后进 链表二叉树
  4. 最后主攻 双指针二分滑动窗口DFSBFS回溯贪心DP

6. 总结

如果把这份笔记压缩成一句话:

  • 语言基础解决“代码怎么写”
  • STL 容器解决“数据怎么存”
  • 数据结构解决“题目对象长什么样”
  • 算法思想解决“题目怎么做”