C++11的一些语法
vector 的用法
在 C++ 中,std::vector
是一个动态数组,它可以在运行时调整大小,std::vector
是 C++ 标准模板库 (STL) 中的一个重要容器类。
基本用法
在使用 std::vector
之前,需要包含头文件 <vector>
。
#include <iostream>
#include <vector>
当然,现在包含万能头 <bits/stdc++.h> 文件也是可以的。
1. 创建和初始化 std::vector
可以使用不同的方式来创建和初始化 std::vector
:
int main() {
// 创建一个空的 vector
std::vector<int> vec1;
// 使用初始大小(默认值为0)
std::vector<int> vec2(5); // 包含5个默认值(通常是0)
// 使用初始大小和指定值
std::vector<int> vec3(5, 10); // 包含5个值为10的元素
// 使用初始化列表
std::vector<int> vec4 = {1, 2, 3, 4, 5};
return 0;
}
2. 添加和删除元素
std::vector
提供多种方法来添加和删除元素:
int main() {
std::vector<int> vec;
// 添加元素
vec.push_back(1);
vec.push_back(2);
vec.push_back(3); // 现在 vec 包含 {1, 2, 3}
// 在指定位置插入元素,
vec.insert(vec.begin() + 1, 4); // 在索引1插入4,现在 vec 为 {1, 4, 2, 3}
// 注意:插入元素是O(N)的时间复杂度,因为在插入之前需要移动该位置及右边的所有元素。
// 删除元素
vec.pop_back(); // 删除最后一个元素,现在 vec 为 {1, 4, 2}
vec.erase(vec.begin() + 1); // 删除索引1的元素,现在 vec 为 {1, 2}
//删除元素也是$O(n)$的时间复杂度
return 0;
}
3. 访问元素
可以使用下标运算符或迭代器访问 std::vector
中的元素:
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用下标运算符访问元素
std::cout << "First element: " << vec[0] << std::endl; // 输出: First element: 1
// 使用 `.at()` 方法访问元素(带边界检查)
std::cout << "Second element: " << vec.at(1) << std::endl; // 输出: Second element: 2
// 使用迭代器访问元素
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;
return 0;
}
4. 常用成员函数
(1)、 resize(size_type count)
:更改 vector
的大小。
resize 用于调整 vector 的大小。
如果新的大小大于当前大小,vector 将会增加元素以达到这个新大小,新增的元素会被初始化为默认值。
如果新的大小小于当前大小,vector 将会删除多余的元素。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3};
// 调整大小为 5,新增的元素为 0
vec.resize(5);
std::cout << "After resize to 5: ";
for (int num : vec) {
std::cout << num << " "; // 输出: 1 2 3 0 0
}
std::cout << std::endl;
// 调整大小为 2,丢弃后面的元素
vec.resize(2);
std::cout << "After resize to 2: ";
for (int num : vec) {
std::cout << num << " "; // 输出: 1 2
}
std::cout << std::endl;
return 0;
}
(2)、 capacity()
:返回当前分配的容量。
capacity 返回 vector 当前可以容纳的元素数量,而不需要重新分配内存。它可能大于或等于 vector 的大小,表示内存的预分配。
#include <bits/stdc++.h>
int main() {
std::vector<int> vec;
// 添加一些元素
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
std::cout << "Size: " << vec.size() << std::endl; // 输出: 3
std::cout << "Capacity: " << vec.capacity() << std::endl; // 输出可能是 4 或更大
// 继续添加元素,超出当前容量
vec.push_back(4);
std::cout << "Size after adding more: " << vec.size() << std::endl; // 输出: 4
std::cout << "Capacity after adding more: " << vec.capacity() << std::endl; // 输出可能是 4 或更大
return 0;
}
(3)、clear()
:移除所有元素。
清空 vector
中的所有元素,使得大小变为 0,但不会改变 capacity
。
(4)、empty()
:检查 vector
是否为空。
判断 vector
是否为空。如果 vector
的大小为 0,返回 true
;否则返回 false
。
(5)、size()
:返回当前元素数量。
示例
```cpp
#include <iostream>
#include <vector>
int main() {
// 创建一个 vector
std::vector<int> vec;
// 检查 vector 是否为空
if (vec.empty()) {
std::cout << "Vector is empty." << std::endl; // 输出: Vector is empty.
}
// 向 vector 添加一些元素
vec.push_back(5);
vec.push_back(10);
vec.push_back(15);
// 检查 vector 的状态
std::cout << "Size: " << vec.size() << std::endl; // 输出: Size: 3
std::cout << "Capacity: " << vec.capacity() << std::endl; // 可能输出: Capacity: 4 或更大
std::cout << "Is vector empty? " << (vec.empty() ? "Yes" : "No") << std::endl; // 输出: No
// 清空 vector
vec.clear();
std::cout << "After clear -> Size: " << vec.size() << std::endl; // 输出: After clear -> Size: 0
std::cout << "Is vector empty? " << (vec.empty() ? "Yes" : "No") << std::endl; // 输出: Yes
// 再次检查容量
std::cout << "Capacity after clear: " << vec.capacity() << std::endl; // 输出容量可能保持不变
return 0;
}
```
### 输出结果
运行以上代码,将会得到以下输出(具体的 `capacity` 值可能因编译器和环境而异):
```
Vector is empty.
Size: 3
Capacity: 4
Is vector empty? No
After clear -> Size: 0
Is vector empty? Yes
Capacity after clear: 4
```
- 在开始时,
vector
为空,使用empty()
返回true
。 - 添加元素后,
empty()
返回false
,且size()
返回当前元素数量。 - 调用
clear()
后,vector
中的所有元素被移除,size()
变为 0,empty()
返回true
,但capacity
可能不变。
小结
std::vector
是 C++ 中非常有用的容器,适合存储不定数量的元素。它提供了灵活的操作和高效的访问方式,适合动态处理数据。
它具有如下特点:
- 支持数组下标
- 可以动态调整大小
- 比数组稍慢
- 有许多自定义函数(方法)可以使用
- 支持插入和删除操作,但如果插入和删除的位置不在末尾,则比较慢
C++ 中list的用法
在 C++ 中,std::list
是一个双向链表容器,它提供了快速的插入和删除操作,适合需要频繁插入和删除元素的场景。与 std::vector
不同,std::list
的元素在内存中不是连续存储的,因此访问元素的速度相对较慢,但在对元素进行插入和删除时表现得更有效率。
基本用法
在使用 std::list
之前,需要包含头文件 <list>
。
#include <iostream>
#include <list>
1. 创建和初始化 std::list
可以使用不同的方式来创建和初始化 std::list
:
int main() {
// 创建一个空的 list
std::list<int> myList;
// 使用初始大小和指定值
std::list<int> myList2(5, 10); // 将包含5个值为10的元素
// 使用初始化列表
std::list<int> myList3 = {1, 2, 3, 4, 5};
return 0;
}
2. 添加和删除元素
std::list
提供多种方法来添加和删除元素:
push_back(); //在末尾插入
push_front(); //在开头插入
pop_back(); //在末尾删除
pop_front(); //在开头删除
remove(a); //删除值为a的元素
示例:
int main() {
std::list<int> myList;
// 添加元素
myList.push_back(1); // 在末尾添加
myList.push_back(2);
myList.push_front(0); // 在开头添加
// 现在 myList 为 {0, 1, 2}
// 删除元素
myList.pop_back(); // 删除末尾元素,现在 myList 为 {0, 1}
myList.pop_front(); // 删除开头元素,现在 myList 为 {1}
myList.remove(1); // 删除值为1的元素,现在 myList 为空
return 0;
}
3. 访问元素
(1)、std::list
不支持随机访问(如下标运算符),但可以使用迭代器访问元素:
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
// 使用迭代器访问元素
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;
return 0;
}
(2)、迭代器也支持自减运算, 可以从后往前访问链表,但是要注意初始值不能是end()
,
因为end()
表示的是链表尾部元素的下一个位置,那个位置是没有元素的**
#include <iostream>
#include <list>
int main() {
// 创建一个 list,并添加一些元素
std::list<int> myList = {1, 2, 3, 4, 5};
// 使用迭代器从 list 的末尾迭代到开头
for (auto p = myList.end(); p != myList.begin();) {
--p; // 先将迭代器减少到最后一个有效元素
std::cout << *p << " "; // 输出: 5 4 3 2 1
}
std::cout << std::endl;
return 0;
}
(3)、 反向迭代器
可以使用 std::list 的反向迭代器 rbegin() 和 rend() 进行反向访问。
#include <iostream>
#include <list>
int main() {
// 创建一个 list,并添加一些元素
std::list<int> myList = {1, 2, 3, 4, 5};
// 使用 auto 和反向迭代器反向访问 list
std::cout << "Elements in the list in reverse order:" << std::endl;
for (auto it = myList.rbegin(); it != myList.rend(); ++it) {
std::cout << *it << " "; // 输出: 5 4 3 2 1
}
std::cout << std::endl;
return 0;
}
4. 常用成员函数
size()
:返回当前元素数量。empty()
:检查list
是否为空。clear()
:移除所有元素。sort()
:对元素进行排序。reverse()
:反转列表。
int main() {
std::list<int> myList = {5, 2, 3, 1, 4};
// 排序列表
myList.sort(); // 现在 myList 为 {1, 2, 3, 4, 5}
// 反转列表
myList.reverse(); // 现在 myList 为 {5, 4, 3, 2, 1}
std::cout << "Size: " << myList.size() << std::endl; // 输出: Size: 5
// 清空列表
myList.clear();
std::cout << "Size after clear: " << myList.size() << std::endl; // 输出: Size after clear: 0
if (myList.empty()) {
std::cout << "List is empty." << std::endl;
}
return 0;
}
小结
std::list
是 C++ 中非常有用的容器,适合需要频繁插入和删除的场景。它提供了灵活的操作,但由于不支持随机访问,相较于 std::vector
在访问元素时可能会慢一些。了解其基本用法和成员函数,能够帮助更好地管理动态数据结构。
三、 C++ queue的用法
在 C++ 中,std::queue
是一个适用于先进先出(FIFO)原则的容器适配器,它通常用于处理数据流,确保元素以添加的顺序被访问。std::queue
只能在队尾添加元素(enqueue),并在队头删除元素(dequeue)。
要使用 std::queue
,需要包含头文件 <queue>
。
基本用法
#include <iostream>
#include <queue>
int main() {
// 创建一个空的 queue
std::queue<int> myQueue;
// 向队列中添加元素(enqueue)
myQueue.push(1);
myQueue.push(2);
myQueue.push(3); // 现在队列为 {1, 2, 3}
// 访问队列的头元素
std::cout << "Front element: " << myQueue.front() << std::endl; // 输出: 1
// 删除队列的头元素(dequeue)
myQueue.pop(); // 现在队列为 {2, 3}
// 再次访问头元素
std::cout << "Front element after pop: " << myQueue.front() << std::endl; // 输出: 2
// 检查队列是否为空
if (!myQueue.empty()) {
std::cout << "Queue size: " << myQueue.size() << std::endl; // 输出: 2
}
// 清空队列
while (!myQueue.empty()) {
myQueue.pop(); // 逐步删除元素
}
std::cout << "Queue size after clearing: " << myQueue.size() << std::endl; // 输出: 0
return 0;
}
常用成员函数
push(const T& val)
:在队列的末尾添加一个元素。pop()
:删除队列的头元素。front()
:访问队列的头元素。back()
:访问队列的尾元素。empty()
:检查队列是否为空。size()
:返回队列中元素的数量。
实际应用示例
#include <iostream>
#include <queue>
int main() {
std::queue<std::string> taskQueue;
// 添加任务到队列中
taskQueue.push("Task 1");
taskQueue.push("Task 2");
taskQueue.push("Task 3");
// 执行队列中的任务
while (!taskQueue.empty()) {
std::cout << "Processing: " << taskQueue.front() << std::endl; // 访问头元素
taskQueue.pop(); // 执行完任务后移除
}
return 0;
}
优先队列的用法
在 C++ 中,std::priority_queue
是一种基于堆(heap)实现的容器适配器,它允许我们以优先级的方式访问元素。std::priority_queue
可以用于实现最大堆或最小堆,其默认行为是构建最大堆,确保最大的元素总是在队列的顶部。
要使用 std::priority_queue
,需要包含头文件 <queue>
。
1、基本用法
(1)、默认是大根堆,
std::priority_queue<int> myque1; //定义整数大根堆
std::priority_queue<double> myque2; // 定义double类型的大根堆
大根堆实例:
#include <iostream>
#include <queue>
int main() {
// 创建一个空的优先队列
std::priority_queue<int> pq;
// 添加元素(默认是最大堆)
pq.push(10);
pq.push(30);
pq.push(20);
// 输出优先队列的顶端元素(最大元素)
std::cout << "Top element: " << pq.top() << std::endl; // 输出: 30
// 删除顶端元素
pq.pop(); // 移除最大元素
std::cout << "Top element after pop: " << pq.top() << std::endl; // 输出: 20
// 检查优先队列是否为空
if (!pq.empty()) {
std::cout << "Size of the priority queue: " << pq.size() << std::endl; // 输出: 2
}
// 清空优先队列
while (!pq.empty()) {
pq.pop(); // 逐步删除元素
}
std::cout << "Size after clearing: " << pq.size() << std::endl; // 输出: 0
return 0;
}
(2)、自定义优先级
priority_queue<int, vector<int>, less<int>> myq1; //定义整数大根堆
priority_queue<int, vector<int>, greater<int>>; //定义整数小根堆
priority_queue<doulbe, vector<double>, greater<double>>; //定义double类型的小根堆
struct node{
int a;
bool operator < (const node &t) const{
return a > t.a;
}
};
std::priority_queue<node> myq; // a越小优先级越高
小根堆示例:
#include <iostream>
#include <list>
#include <queue>
using namespace std;
struct node{
int a;
node(int t = 0) {a = t;}
bool operator < (const node &t) const{
return a > t.a;
}
};
int main() {
priority_queue<node> myq; // a越小优先级越高
myq.push(node(5));
myq.push(node(3));
myq.push(node(9));
while(!myq.empty()){
cout << myq.top().a << ' ' ; //输出3 5 9
myq.pop();
}
return 0;
}
常用成员函数
push(const T& value)
:在优先队列中插入一个新元素。pop()
:删除优先队列中最高优先级的元素(顶部元素)。top()
:访问最高优先级的元素。empty()
:检查优先队列是否为空。size()
:返回优先队列中元素的数量。
map的使用
C++中的std::map
是一个关联容器,用于存储键值对(key-value pair)。这些键是唯一的,使用键可以快速查找对应的值。std::map
通常实现为红黑树,以保持元素的有序性,使其支持对数时间复杂度的插入、删除和查找操作。
基本特性
- 键唯一:每个键只能出现一次。
- 有序性:元素按照键的顺序排列。
- 键值对:存储的元素为键值对,可以通过键来访问值。
包含头文件
在使用std::map
之前,需要包含 <map>
头文件:
#include <map>
创建和初始化
可以使用花括号初始化,或者通过 insert
方法添加元素:
#include <iostream>
#include <map>
int main() {
// 创建一个空的 map
std::map<int, std::string> myMap;
// 使用 insert 添加元素
myMap.insert(std::make_pair(1, "Apple"));
myMap.insert(std::pair<int, std::string>(2, "Banana"));
// 使用花括号初始化
std::map<int, std::string> anotherMap{
{3, "Cherry"},
{4, "Date"}
};
return 0;
}
插入元素
可以使用 insert
方法或 []
运算符插入元素:
myMap[3] = "Orange"; // 使用 [] 运算符插入元素
访问元素
可以使用 []
运算符或 at()
方法访问元素:
std::cout << "Key 1: " << myMap[1] << std::endl; // 输出: Apple
std::cout << "Key 2: " << myMap.at(2) << std::endl; // 输出: Banana
注意,当使用 []
运算符访问不存在的键时,会插入一个默认值(如空字符串),而 at()
方法会抛出异常。
删除元素
可以使用 erase
方法删除元素:
myMap.erase(2); // 删除键为2的元素
查找元素
使用 find
方法来查找元素,返回一个迭代器。如果找到了元素,迭代器指向该元素;如果未找到,则指向 end()
:
auto it = myMap.find(1);
if (it != myMap.end()) {
std::cout << "Found: " << it->second << std::endl; // 输出: Found: Apple
} else {
std::cout << "Not found!" << std::endl;
}
遍历元素
可以使用范围 for
循环或迭代器来遍历 map
:
for (const auto &pair : myMap) {
std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
}
其他操作
- 大小:可以使用
size()
方法获取元素数量。 - 空检查:通过
empty()
方法检查 map 是否为空。
完整示例
以下是一个完整的示例,展示了上述所有特性:
#include <iostream>
#include <map>
int main() {
// 创建一个 map
std::map<int, std::string> myMap;
// 插入元素
myMap.insert({1, "Apple"});
myMap[2] = "Banana";
myMap[3] = "Cherry";
// 访问元素
std::cout << "Key 1: " << myMap[1] << std::endl; // 输出: Apple
// 删除元素
myMap.erase(2); // 删除键为2的元素
// 查找元素
auto it = myMap.find(2);
if (it != myMap.end()) {
std::cout << "Found: " << it->second << std::endl;
} else {
std::cout << "Key 2 not found!" << std::endl; // 输出: Key 2 not found!
}
// 遍历元素
for (const auto &pair : myMap) {
std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
}
return 0;
}
set的使用
C++中的std::set
是一个关联容器,用于存储唯一的元素。std::set
是自动排序的,元素是以特定顺序存储的,且不允许重复元素。这个容器的实现通常是基于红黑树,因此支持\(O(logN)\)时间复杂度的插入、删除和查找操作。
基本特性
- 唯一性:集合中的元素是唯一的,不允许存储重复的值。
- 排序性:元素自动按升序(默认)排列,插入的顺序并不保证。
- 不可修改性:集合中的元素是只读的,不能直接修改已有的元素。
包含头文件
在使用 std::set
之前,需要包含 <set>
头文件:
#include <set>
创建和初始化
可以通过多种方式创建 std::set
,例如:使用默认构造函数,使用花括号初始化等。
#include <iostream>
#include <set>
int main() {
// 创建一个空的 set
std::set<int> mySet;
// 使用 insert 添加元素
mySet.insert(10);
mySet.insert(20);
mySet.insert(30);
//mySet:{10, 20, 30}
// 使用花括号初始化
std::set<int> Set2{3, 1, 4, 1, 5}; // Set2:{1,3,4,5}
return 0;
}
插入元素
可以使用 insert
方法添加元素:
mySet.insert(15); // 插入新元素
注意,如果尝试插入一个已存在的元素,insert
不会变化集合,并返回一个指向该元素的迭代器。
删除元素
可以使用 erase
方法删除元素:
mySet.erase(20); // 删除元素20
查找元素
使用 find
方法来查找元素。如果找到了,返回指向该元素的迭代器,否则返回 end()
:
auto it = mySet.find(10);
if (it != mySet.end()) {
std::cout << "Found: " << *it << std::endl; // 输出: Found: 10
} else {
std::cout << "Not found!" << std::endl;
}
遍历元素
可以使用范围 for
循环或迭代器来遍历 set
:
for (const auto &elem : mySet) {
std::cout << elem << " "; // 输出: 10 15 30
}
大小和空检查
可以使用 size()
方法获取元素数量,也可以使用 empty()
方法检查 set
是否为空:
std::cout << "Size: " << mySet.size() << std::endl; // 输出: Size: 3
if (mySet.empty()) {
std::cout << "Set is empty!" << std::endl;
}
合并集合
可以通过 insert
方法将一个集合的元素插入到另一个集合中,自动处理重复值:
mySet.insert(anotherSet.begin(), anotherSet.end());
完整示例
以下是一个完整的示例,展示了上述所有特性:
#include <iostream>
#include <set>
int main() {
// 创建一个 set
std::set<int> mySet;
// 插入元素
mySet.insert(10);
mySet.insert(20);
mySet.insert(30);
mySet.insert(20); // 尝试插入重复元素,忽略
// 删除元素
mySet.erase(20); // 删除元素20
// 查找元素
auto it = mySet.find(10);
if (it != mySet.end()) {
std::cout << "Found: " << *it << std::endl; // 输出: Found: 10
} else {
std::cout << "Not found!" << std::endl;
}
// 遍历元素
std::cout << "Elements in mySet: ";
for (const auto &elem : mySet) {
std::cout << elem << " "; // 输出: 10 30
}
std::cout << std::endl;
// 查看大小和空状态
std::cout << "Size of mySet: " << mySet.size() << std::endl;
if (mySet.empty()) {
std::cout << "mySet is empty!" << std::endl;
} else {
std::cout << "mySet is not empty!" << std::endl;
}
return 0;
}
小结
std::set
是一个强大的数据结构,适用于需要高效查找和无重复元素的场景,如存储唯一值、集合操作等。掌握其基本用法可以帮助开发者高效管理和操作集合数据。
C++中匿名函数的使用
在 C++ 中,匿名函数通常被称为“Lambda 表达式”。它们允许在代码中定义未命名的函数,通常用于短小的、临时的功能。Lambda 表达式在 C++11 标准中引入,并在之后的标准中得到了增强。
Lambda 表达式的基本语法
[capture](parameters) -> return_type {
// function body
}
capture
:捕获外部变量的方式,可以是按值捕获或按引用捕获。parameters
:参数列表,如同普通函数。return_type
:返回类型,可以省略,编译器会自动推断。function body
:函数体。
示例
以下是一些使用 Lambda 表达式的示例。
1. 简单的 Lambda 表达式
#include <iostream>
int main() {
auto greet = []() {
std::cout << "Hello, World!" << std::endl;
};
greet(); // 调用 Lambda 表达式
return 0;
}
2. 带参数的 Lambda 表达式
#include <iostream>
int main() {
auto add = [](int a, int b) {
return a + b;
};
int sum = add(5, 3);
std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 8
return 0;
}
3. 捕获外部变量
#include <iostream>
int main() {
int x = 10;
int y = 20;
auto add = [x, y]() { // 按值捕获 x 和 y
return x + y;
};
std::cout << "Sum: " << add() << std::endl; // 输出: Sum: 30
return 0;
}
4. 按引用捕获
#include <iostream>
int main() {
int x = 10;
auto increment = [&x]() { // 按引用捕获 x
x++;
};
increment();
std::cout << "Incremented x: " << x << std::endl; // 输出: Incremented x: 11
return 0;
}
5. 结合 STL
Lambda 表达式常与 STL 算法结合使用,例如 std::sort
:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {5, 3, 8, 1, 2};
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a < b; // 自定义排序:升序
});
for (const auto &value : vec) {
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}
小结
Lambda 表达式在 C++ 中提供了一种方便且灵活的方式来定义匿名函数,能够简化代码并提高可读性。在使用时,需要合理选择捕获方式(按值或按引用)以避免潜在的错误。