C++知识
指针
*号的两种使用定义指针
int* p, int *p,星号位置不重要解指针
p = &a ,*(p)指针可用
->访问成员变量,ptr->member等价于(*ptr).member也可以连续访问
root->left->right
1 | |
new关键字返回的是指针
所以构造结点的正确做法如下
1 | |
引用,与目标共享一个内存空间,修改引用,也会修改目标
1 | |
迭代器
迭代器是一种泛化的指针,用于统一访问各种容器中的元素
所有标准容器都有迭代器
为什么返回迭代器而不是索引?因为不是所有容器都有索引,迭代器是通用位置的抽象
实质是一个指针,可用
it -> first, it -> second访问内部成员也可以完全解指针
&a = *it, a -> first用于迭代
for (auto it = c.begin(); it != c.end(); ++it)或者for (const auto& x : vec)类型复杂所以直接用 auto 声明
很多函数返回的都是迭代器,就比如
lower_bound(), map.find(key), nums.begin(), nums.end()
vector 向量(动态数组)
vector<int> vec(n, 1);初始化长度为 n,元素全为 1 的向量vector<vector<int>> vec;空的二维向量int m = matrix.size(), n = matrix[0].size();获取向量的长度 m,每一项的长度为 nvec.push_back({b});添加每一项的第一个vec[i].push_back(b);添加每一项的第二个vec.resize(n);初始化,分配空间之后才能赋值,所有初值为 0swap(nums[i], nums[j]);交换两个元素vec[0], vec.back()返回第一个和最后一个元素的引用vec.begin(), vec.end()返回第一个和最后一个元素的迭代器ranges::reverse(nums.begin(), nums.begin() + k);翻转向量,123->321ranges::sort(vec);排序数组ranges::sort(vec.begin(), vec.end());排序指定区间的数组auto it = v.begin() + index; 通过索引返回迭代器vec.erase(it);动态删除向量里的元素vec1 = move(vec2);把 vec2 内部的指针等资源直接转移给 vec1,vec2 空,时间复杂度 O(1),vec1 会被完全覆盖vec.emplace_back(pair)功能相当于vec.push_back(pair),但在面对复杂对象时效率更高,他是直接在 vec 里面创建这个对象vector vec(n, string(m, '.'));快捷创建 n 行 m 列二维向量,内部是 string,前面vector memo(n, vector<int>(amount + 1, -1));快捷创建二维数组,前面的 vector 不用写<>也行,C++会自己推导
1 | |
二维向量相关操作
1 | |
unordered_map 哈希表
unordered_map<int, int> map;初始化map[i] == map2[i]用操作符[]访问不存在的 key 时会隐式的插入 0 污染 map,即使作为判断条件也是map.count(key)判断是否存在,返回 0 或 1,在上面的判断条件里加上map2.count(s[i]) &&先执行就能避免污染- map 是无序的,用
for (auto& x : map)迭代也是随机的
lambda 表达式(匿名函数)
C++在方法内定义方法就要用这种隐函数的语法
函数名只能用 auto[capture](parameters) -> return-type { body }
1 | |
捕获器类型
- [] 不捕获任何外部变量
- [&] 按引用捕获所有用到的外部变量(推荐用于读写或大对象)
- [=] 按值捕获所有用到的外部变量(只读副本)
- [&nums, threshold] 显式按引用捕获 nums,按值捕获 threshold
定义递归函数,把函数自身作为参数传进去(this auto&& dfs)
1 | |
<<>>位运算符
<<运算符用于左移,>>运算符用于右移
1 | |
&运算符用于位与,|运算符用于位或,^运算符用于位异或,~运算符用于位取反
1 | |
deque 双端队列
两侧都能添加和删除元素deque<int> q;初始化pop_front() 删除队列首元素,动态删除,删完之后队首元素索引还是 0pop 访问队首元素,但不删除
条件运算顺序
C++ 的&&是从左到右求值,所以会先执行q.back(),再判断!q.empty(),在 q 为空的时候这么做会报错
正确做法:while (!q.empty() && nums[i] > nums[q.back()])
字符串
操作和动态数组很像
可以直接加减字符串
string ans = t + 'a'字符串是可变的,但在作为 key 的时候存储的是字符串的副本,所以修改原来的字符串不影响
构建子串
std::string sub1 = s.substr(0, 5); // 选取从 0 开始的长度为 5 的子串
std::string sub2 = s.substr(7); // 选取从 7 开始的子串
stack 栈
stack<int> s;初始化s.push(x)入栈s.pop()出栈s.top()访问栈顶元素,要获取栈顶再弹出就要先 top()再 pop()s.empty()判断栈是否为空st = stack<int>();构造空栈,置空栈
单调栈,栈内元素是单调的
1 | |
queue 队列
先进后出,从队尾入队,队首出队(就是排队)
可用于广度优先搜索(BFS)
queue<T> q;初始化q.push(x)入队q.pop()出队q.front()访问队首元素,要获取队首再弹出就要先 front()再 pop()q.empty()判断队列是否为空
Deque 双端队列
构造函数的初始化列表
用初始化列表初始化成员变量,比赋值更高效
const 成员和引用成员只能用初始化列表初始化
- LRUCache(int _capacity) 是构造函数的参数列表;
- :capacity(_capacity),size(0) 为初始化列表
1 | |
结构体
结构体(struct)是一种用户自定义的数据类型,允许将不同类型的数据组合在一起。
结点就是一种定义的结构体,用指针加成员访问符->可以访问成员变量,用结构体名加.也可以访问成员变量
1 | |
pair 二元组
pair 是 C++ 标准库中的一个类,用于存储两个元素。
适合遍历图,Map,表示坐标,键值对
有两个成员变量 first 和 second,分别表示键和值。
vector<pair<int, int>> q;坐标向量for (auto& [x, y] : q)结构化绑定遍历
或者for (auto& p : q)然后用 p.first, p.second 访问
Map 哈希表
map 里面的元素是键值对,pair 来表示,也可称作 entry
first 和 second 是 pair 的成员变量
map 的遍历for (auto& [key, value] : map)结构化绑定,里面是形参代表的键值for (auto& p : map)然后用 p.first, p.second 访问`
堆(优先队列)
priority_queue<int> pq;最大堆,top()是最大值priority_queue<int, vector<int>, greater<int>> minHeap;最小堆,top()是最小值
以下是优先队列(priority_queue)的操作总结表:
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
push(x) |
O(log n) | 插入元素 |
pop() |
O(log n) | 删除堆顶元素(无返回值!) |
top() |
O(1) | 返回堆顶元素(不删除) |
empty() |
O(1) | 判断是否为空 |
size() |
O(1) | 返回元素个数 |
注意事项:
- ❌ 不能遍历:没有迭代器,不能 for-each。
- ❌ 不能随机访问:只能通过
top()访问堆顶。 - ❌ 不能修改内部元素:一旦插入,只能通过
pop()删除。 - ❌ 不能高效查找或删除任意元素:不是设计用来干这个的。
set
unordered_set<int> mySet;// 初始化
// 用 vector 的 begin() 和 end() 初始化 unordered_setunordered_set<int> mySet(vec.begin(), vec.end());插入元素
1
2
3mySet.insert(10);
mySet.insert(20);
mySet.insert(10); // 重复插入无效,仍只存一个 10查找元素
1
2
3
4
5
6
7if (mySet.find(10) != mySet.end()) {
cout << "Found 10\n";
}
// 或者用 count(返回 0 或 1)
if (mySet.count(10)) {
cout << "10 exists\n";
}删除元素
mySet.erase(10); // 删除值为 10 的元素