零碎
字符串
ASCII码表
常见ASCII范围
小写字母在大写字母后面,因此大写字母转换为小写字母需要+32
数字 0-9 |
48-57 | '0' =48, '9' =57 |
大写字母 A-Z |
65-90 | 'A' =65, 'Z' =90 |
小写字母 a-z |
97-122 | 'a' =97, 'z' =122 |
空格 | 32 | ' ' |
判断是否是大写/小写字母
- c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'
- str[i] >= 65 && str[i] <= 90 || str[i] >= 97 && str[i] <= 122
- tolower或toupper
- 加减32手动转换
字符与数字互转
-
字符转数字:
'0'-'9'
→0-9
char c = '7'; int num = c - '0'; // num = 7
-
数字转字符:
0-9
→'0'-'9'
int num = 5; char c = num + '0'; // c = '5'
字符串转数值 stoi
/ stol
string s = "456";
int num = stoi(s); // 456
字符串流
字符串流 #include <sstream> 字符串与数据类型的转换(istringstream读字符串,ostringstream写字符串)
基本步骤
- 将字符串绑定到输入流:
istringstream iss(str);
- 按需提取数据:使用
>>
操作符(自动跳过空白符)。 - 处理剩余数据或错误:检查流状态。
string line = "100 3.14 hello";
istringstream iss(line);
int a;
double b;
string c;
iss >> a >> b >> c; // a=100, b=3.14, c="hello"
string line = "2023,12,31";
istringstream iss(line);
int year, month, day;
char comma;
iss >> year >> comma >> month >> comma >> day; // 显式读取逗号
循环读取未知数量的数据
string line = "1 2 3 4 5";
istringstream iss(line);
vector<int> nums;
int x;
while (iss >> x) { // 直到流无法提取数据
nums.push_back(x);
}
// nums = [1,2,3,4,5]
输入一行 "Name:Alice Age:25 Score:95.5"
,提取姓名、年龄、分数。
string line = "Name:Alice Age:25 Score:95.5";
istringstream iss(line);
string name, tmp;
int age;
double score;
iss >> tmp >> name >> tmp >> age >> tmp >> score;
// name="Alice", age=25, score=95.5
string的用法
substr, find, insert, replace, erase,并且与vector一样都有push_back和pop_back方法
size() / length() |
返回字符串长度 | int len = s.size(); |
empty() |
判断字符串是否为空 | if (s.empty()) { ... } |
clear() |
清空字符串内容 | s.clear(); |
push_back(char c) |
尾部添加字符 | s.push_back('!'); |
pop_back() |
删除尾部字符 | s.pop_back(); |
substr(pos, len) |
截取子字符串 | string sub = s.substr(0, 3); |
find(str) |
查找子串位置(返回索引或 npos ) |
int pos = s.find("abc"); |
replace(pos, len, new_str) |
替换子串 | s.replace(2, 2, "xyz"); |
append(str) |
追加字符串 | s.append("world"); |
insert(pos, str) |
在指定位置插入字符串 | s.insert(5, " "); |
erase(pos, len) |
删除子串 | s.erase(3, 2); |
-
insert插入字符串
string& insert(size_t pos, const string& str); // 在位置 pos 处插入字符串 str string& insert(size_t pos, size_t n, char c); // 在位置 pos 处插入 n 个字符 c,即插入多个重复字符(n=1就是一个) void insert(iterator p, InputIterator first, InputIterator last); // 在迭代器 p 处插入范围 [first, last) 的字符
第一个参数是插入位置(下标或迭代器),后面参数是插入字符串的首尾迭代器/目标字符串
#include <iostream> #include <string> using namespace std; int main() { string str = "abcxyz", str2 = "opq"; // 在str的第4个位置插入str2即opq,后面两个参数是待插字符串的首尾迭代器,左闭右开 str.insert(str.begin() + 3, str2.begin(), str2.end()); cout << str << endl; return 0; }
第一个参数表示在何处插入,第二个参数表示插入的字符的数量
// 在高位补0对齐位数 while (s1.size() < s2.size()) { s1.insert(0, 1, '0'); } while (s1.size() > s2.size()) { s2.insert(0, 1, '0'); }
-
erase删除单个字符或一个区间内字符
string& erase(size_t pos = 0, size_t len = npos); // 删除从位置 pos 开始的 len 个字符(默认删除到末尾) iterator erase(const_iterator p); // 删除迭代器 p 指向的字符 iterator erase(const_iterator first, const_iterator last); // 删除 [first, last) 范围内的字符
一个参数是删除一个字符;
两个参数是将首尾迭代器内的字符全部删除,或从下标开始删除len个字符
#include <iostream> #include <string> using namespace std; int main() { string str = "abcdefg"; // 从第3个字符一直删到最后一个字符,左闭右开,只剩下头2个字符和最后一个字符 str.erase(str.begin() + 2, str.end() - 1); cout << str << endl; return 0; }
#include <iostream> #include <string> using namespace std; int main() { string str = "abcdefg"; // 从第4个位置开始删除2个字符 str.erase(3, 2); cout << str << endl; return 0; }
-
查找
size_t find(const string& str, size_t pos = 0) const; // 默认一个参数从头开始找,也可以指定第二个参数参数从pos开始找
查找返回起始下标,没找到返回string::npos(一个参数目标字符串是从起始位置开始查找返回第一次出现的下标,两个参数从start_pos开始找目标字符串)
while ((pos = s.find(temp, pos)) != string::npos) { count[temp]++; pos++; }
-
替换
string& replace(size_t pos, size_t len, const string& str); // 将字符串从位置 pos 开始的 len 个字符替换为 str string& replace(const_iterator first, const_iterator last, const string& str); // 替换迭代器 [first, last) 范围内的内容为 str
替换是从第一个参数的下标开始,将长度为第二个参数的子串替换为第三个参数的目标字符串(有可能替换的目标字符串超过子串长度,也可能少于)
#include <iostream> #include <string> using namespace std; int main() { string str = "Maybe you will turn around."; string str2 = "will not"; string str3 = "surely"; // 将str从第11个位置开始的4个长度子串will替换成str2 will not cout << str.replace(10, 4, str2) << endl; // 将str的开头到第6个位置子串Maybe替换成str3 surly,前两个参数左闭右开 cout << str.replace(str.begin(), str.begin() + 5, str3) << endl; return 0; }
find与replace组合使用
string s = "Hello World"; size_t pos = s.find("World"); if (pos != string::npos) { s.replace(pos, 5, "C++"); // 替换"World"为"C++" } // 结果:"Hello C++"
-
截取子串
str.substr(k)从下标为k除截取到末尾的字符串,str.substr(k, m)从下标为k的字符除截取长度为m的字符串
string sub = s2.substr(6, 3); // 从索引6开始截取3字符:"CPP"
典型应用
统计子串出现次数
int count = 0;
size_t pos = 0;
while ((pos = s.find("ab", pos)) != string::npos) {
count++;
pos += 2; // 跳过已匹配的子串
// s.erase(pos, 2); // 或直接删掉
}
删除子串中所有特定字符串
find与erase配合使用,循环删除子串中所有特定字符串
#include <string>
#include <iostream>
using namespace std;
int main() {
string str1, str2;
while (cin >> str1 >> str2) {
int pos = 0;
while ((pos = str1.find(str2)) != string::npos) {
str1.erase(pos, str2.size());
}
cout << str1 << endl;
}
return 0;
}
在高位补0对齐位数
while (s1.size() < s2.size()) {
s1.insert(0, 1, '0');
}
统计单词数
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main() {
string line;
getline(cin, line);
istringstream iss(line);
string word;
int count = 0;
while (iss >> word) { // 自动按空格分割
count++;
}
cout << count << endl;
return 0;
}
统计字符频率
string s;
freq[100] = {0};
for (char c : s) {
freq[c]++;
}
判断回文
bool isPalindrome(const string& s) {
int left = 0, right = s.size() - 1;
while (left < right) {
if (s[left++] != s[right--]) return false;
}
return true;
}
注意事项
-
cin和scanf都是按空格读取
-
用fgets读取一整行需要pop_back()去除末尾的换行符
char buf[10]; fgets(buf, sizeof(buf), stdin); string s; s = buf; s.pop_back();
-
用getline配合cin读取一整行
string s; getline(cin, s);
-
混合输入数字和字符串时,需处理残留的换行符(或使用
getchar();
)int n; cin >> n; cin.ignore(); // 清除换行符 string s; getline(cin, s); int n; scanf("%d", &n); getchar(); char str[20]; fgets(str, 20, stdin);
-
cin >> str
;(或用%s读入字符数组再赋值给string,随后使用string的成员函数) -
cout << str
; (printf("%s\n", str.c_str());
)
常用容器
vector
push_back, insert, pop_back, erase
初始化 | 默认初始化 | vector<int> v1; |
初始为空容器 |
添加元素 | 尾部插入元素 | v.push_back(10); v.emplace_back(10); (C++11+,效率更高) |
动态扩容可能触发内存重新分配(复杂度均摊为O(1)) |
指定位置插入元素 | v.insert(v.begin() + 2, 20); |
插入位置后的元素需后移,避免频繁操作 | |
删除元素 | 尾部删除元素 | v.pop_back(); |
容器非空时使用 |
删除指定位置元素 | v.erase(v.begin() + 1); |
删除位置后的元素需前移 | |
删除指定范围元素 | v.erase(v.begin(), v.begin() + 2); |
范围左闭右开 | |
清空容器 | v.clear(); |
容器变为空,但内存可能保留(可用shrink_to_fit() 释放) |
|
访问元素 | 通过下标访问 | int x = v[0]; |
不检查越界(可能导致未定义行为) |
安全访问(检查越界) | int y = v.at(0); |
越界时抛出out_of_range 异常 |
|
访问首尾元素 | int front = v.front(); int back = v.back(); |
容器非空时使用 | |
遍历容器 | 从头使用迭代器遍历 | for (auto it = v.begin(); it != v.end(); ++it) { ... } |
支持const_iterator (只读遍历) |
从后使用迭代器遍历 | for (auto it = v.rbegin(); it != v.rend(); ++it) { ... } |
支持const_iterator (只读遍历) |
|
范围for循环(C++11+) | for (int num : v) { ... } |
修改元素需用引用:for (int& num : v) |
|
容量操作 | 获取元素数量 | int size = v.size(); |
实际元素个数 |
map
insert, find, erase
insert({key, value}) |
插入键值对 | m.insert({"apple", 2}); |
operator[] |
访问或插入元素(若键不存在则创建) | m["apple"] = 3; |
find(key) |
查找键,返回迭代器(未找到返回 end() ) |
auto it = m.find("apple"); |
count(key) |
统计键是否存在(0或1) | if (m.count("apple")) { ... } |
erase(key) |
删除指定键的元素 | m.erase("apple"); |
size() |
返回元素个数 | int n = m.size(); |
empty() |
判断是否为空 | if (m.empty()) { ... } |
clear() |
清空所有元素 | m.clear(); |
stack与queue
都有push与pop方法、empty和size方法,因为队列队尾入队,队头出队因此有front与back方法,栈仅能访问栈顶元素,只有top方法
map与set
find, insert, erase
pair
下面的操作与map非常像,包括花括号赋值、迭代器遍历first与second、比较大的区别是map默认按键的升序排序
基本声明与初始化
定义 vector
存储 pair
#include <vector>
#include <utility> // pair 的头文件
// 声明一个存储 int-string 键值对的 vector
vector<pair<int, string>> vec;
// 初始化列表直接赋值
vector<pair<int, string>> vec_init = {
{1, "apple"},
{2, "banana"},
{3, "cherry"}
};
添加元素
vec.push_back(make_pair(4, "orange")); // 使用 make_pair
vec.emplace_back(5, "grape"); // 更高效的 emplace_back(C++11)
遍历与访问
通过下标访问
pair<int, string>& first_pair = vec[0];
cout << first_pair.first << ": " << first_pair.second << endl; // 输出 1: apple
遍历所有元素
for (const auto& p : vec) {
cout << p.first << " - " << p.second << endl;
}
使用迭代器
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << it->first << " | " << it->second << endl;
}
容器集合
vector | 动态数组,支持快速随机访问 | push_back() , pop_back() , insert() , erase() , [] , size() , empty() |
需要动态数组、频繁随机访问 |
queue | 先进先出(FIFO)队列 | push() , pop() , front() , back() , size() , empty() |
BFS、缓冲区管理 |
stack | 后进先出(LIFO)栈 | push() , pop() , top() , size() , empty() |
DFS、括号匹配、表达式求值 |
priority_queue | 优先队列(默认最大堆),堆顶元素优先级最高 | push() , pop() , top() , size() , empty() |
Dijkstra算法、贪心策略、Top-K问题 |
deque | 双端队列,两端高效插入/删除,支持随机访问 | push_front() , push_back() , pop_front() , pop_back() , [] , insert() |
滑动窗口、双向操作 |
set | 有序集合(红黑树实现),元素唯一且有序 | insert() , erase() , find() , count() , lower_bound() , upper_bound() |
去重、有序数据维护 |
multiset | 类似set ,但允许重复元素 |
同set |
允许重复的有序集合 |
map | 有序键值对(红黑树实现),键唯一且按序存储 | insert() , erase() , find() , [] , count() , lower_bound() |
字典、键值映射 |
multimap | 类似map ,但允许重复键 |
同map |
允许重复键的映射 |
unordered_set | 哈希集合,元素唯一但无序 | insert() , erase() , find() , count() |
快速去重、无需顺序 |
unordered_map | 哈希表,键唯一但无序 | insert() , erase() , find() , [] , count() |
快速键值查找 |
pair | 存储两个值的模板类,常用于组合数据 | 直接访问first 和second 成员 |
存储坐标、键值对(如边权) |
string | 字符串容器,支持动态操作 | append() , substr() , find() , push_back() , size() , empty() |
字符串处理、文本操作 |
常用的STL算法
比较常用的有sort, find, reverse, swap, erase
sort | 对序列进行排序(默认升序) | sort(v.begin(), v.end(), cmp) |
无返回值,直接修改原序列 | 通用排序 |
stable_sort | 稳定排序(相等元素顺序不变) | stable_sort(v.begin(), v.end(), cmp) |
同上 | 需要保留相等元素原始顺序 |
lower_bound | 在有序序列中返回第一个≥目标值的迭代器 | lower_bound(v.begin(), v.end(), x) |
指向目标位置的迭代器 | 二分查找、范围查询 |
upper_bound | 在有序序列中返回第一个>目标值的迭代器 | upper_bound(v.begin(), v.end(), x) |
同上 | 范围查询、统计出现次数 |
binary_search | 检查有序序列中是否存在某个值 | binary_search(v.begin(), v.end(), x) |
返回bool (存在为true ) |
快速存在性判断 |
find | 在序列中查找第一个等于目标值的元素 | find(v.begin(), v.end(), x) |
指向该元素的迭代器(未找到则返回end() ) |
线性搜索 |
count | 统计序列中等于目标值的元素个数 | count(v.begin(), v.end(), x) |
返回匹配的个数 | 统计频率 |
reverse | 反转序列中的元素顺序 | reverse(v.begin(), v.end()) |
无返回值,直接修改原序列 | 字符串反转、对称操作 |
unique | 移除相邻重复元素(需先排序) | unique(v.begin(), v.end()) |
返回去重后的新逻辑结尾迭代器(需配合erase 使用) |
去重(需先排序) |
next_permutation | 生成序列的下一个字典序排列 | next_permutation(v.begin(), v.end()) |
返回bool (若已是最后排列则返回false ) |
全排列生成(如N皇后问题) |
max_element | 返回序列中最大元素的迭代器 | max_element(v.begin(), v.end()) |
指向最大元素的迭代器 | 找最大值/最小值 |
accumulate | 计算序列的累加值(可自定义操作) | accumulate(v.begin(), v.end(), init, op) |
返回累加结果(init 为初始值,op 为二元操作函数) |
求和、自定义聚合操作 |
fill | 将序列填充为指定值 | fill(v.begin(), v.end(), x) |
无返回值 | 初始化数组 |
copy | 复制序列内容到目标位置 | copy(src.begin(), src.end(), dest.begin()) |
返回目标序列的结束迭代器 | 数组复制 |
swap | 交换两个对象的值 | swap(a, b) |
无返回值 | 变量交换、容器内容交换 |
transform | 对序列中每个元素应用函数并存储结果 | transform(in.begin(), in.end(), out.begin(), op) |
返回输出序列的结束迭代器 | 数据转换(如大小写转换) |
erase | 从容器中删除元素(需结合容器方法使用) | v.erase(it) 或 v.erase(start, end) |
返回删除后的下一个有效迭代器 | 动态删除元素 |
rotate | 循环左移序列元素(如将[a,b,c,d] 变为[b,c,d,a] ) |
rotate(v.begin(), new_start, v.end()) |
无返回值 | 循环移位、字符串旋转 |