持更中。。。仅用作本人的梳理复习,分享出来希望能帮助更多人
常规注意事项
评测语言及版本的选择
评测结果分析
100分
编译错误:语法错
错误0分:测试用例没有一个通过
错误,<100分:测试用例通过了几个
运行错误,0分:内存空间溢出,变量值溢出
运行超时,0分:(死循环)运行超时
<100分:数据规模大、循环次数多
知识点要求
第一题:基本的C/C++代码设计能力,基础数据处理,包括在数组上进行递推、大小比较、计数、排序等。(条件分支+循环+数组)
第二题:理解并熟练编程实现与基本数据结构相关的基础算法,包括递归、排序、查找、字符串等简单处理等。
小知识点
字符与数字的相互转换
关键:ASCII码
1. 将字符转换为对应的整数型数据,只需要将字符数字减去'0'字符,它们的ASCII码的差值即为所求。
char v1='3';
int num=v1 -'0';//将字符‘3’转换成整数型
2. 将数字转换为对应的字符数字,只需加上字符‘0’即可。
int num=1;
char v1 = num + '0';
相关习题:
【CSP CCF记录】201903-2第16次认证 二十四点-CSDN博客
i++和++i的区别
1. 首先,单独拿出来说,i++和++i的意思是一样的,就是i = i + 1。
2. 如果当做运算符来说,就是a = i++ 和 a = ++i这样的形式,情况就不一样了。
- a = i++的意思是,先把i的值赋给a,即a = i,再执行i = i + 1;
- a = ++i是先执行 i = i+1,再把i的值赋给a;
举个例子来说,如果一开始i=4。
那么执行a=i++这条语句之后,a=4,i=5;
那么执行a=++i这条语句之后,i=5,a=5;
同理,i–和--i的用法也是一样的。
3. 另外在循环体中的区别
- for循环中,for(int i = 0;i < 6;i++)和for(int i = 0;i < 6;++i)效果一样
- while(i++)是先用i的初始化值做循环变量再i+1
而while(++i)是先用i的初始值+1,再循环
结构体
定义结构体并对结构体数组进行排序。
例题:【CSP CCF记录】201709-2 第11次认证 公共钥匙盒-CSDN博客
typedef struct{
int w,s,time;//钥匙编号,开始时间,结束时间
}Key;
Key keys[1010];//借钥匙记录
bool cmp(const Key& i1,const Key& i2)
{
return i1.w <i2.w;
}
int main()
{
//...其他内容略
vector<Key> rkeys;//用于存储此时需归还的钥匙
sort(rkeys.begin(),rkeys.end(),cmp);//根据编号从小到大对钥匙进行排序
//...其他内容略
}
矩阵的运算
示例:【CSP CCF记录】202305-2第30次认证 矩阵运算-CSDN博客
矩阵乘法
代码模板:
//tmp1=KtxV
for(int i=0;i<d;i++)
{
for(int j=0;j<d;j++)
{
for(int k=0;k<n;k++)
{
tmp1[i][j]+=Kt[i][k]*V[k][j];
}
//cout<<tmp1[i][j]<<" ";
}
//cout<<endl;
}
性质
- 矩阵乘法不满足交换律,即一般情况下 A×B ≠ B×A。
- 矩阵乘法满足结合律,即 (A×B)×C = A×(B×C)。
- 矩阵乘法满足分配律,即 A×(B+C) = A×B + A×C 和 (A+B)×C = A×C + B×C。
- 如果 A 是一个 m×n 的矩阵,B 是一个 n×p 的矩阵,那么它们的乘积 C 是一个 m×p 的矩阵。
矩阵点乘
推导可得:C·(A×B)=(C·A)×B
矩阵数乘
矩阵的数乘是指将一个矩阵与一个标量(一个单一的数值)相乘。以下是矩阵数乘的一些基本性质:
-
分配律:对于任意的矩阵 A 和标量 c 和 d,以及矩阵的加法,以下等式成立:c(A + B) = cA + cB;(c + d)A = cA + dA
-
结合律:对于任意的矩阵 A 和标量 c 和 d,以下等式成立:(cd)A = c(dA) 这意味着可以先计算标量的乘积,然后再与矩阵相乘,或者先将一个标量与矩阵相乘,然后再将结果与另一个标量相乘,最终结果是相同的。
-
单位元:将矩阵 A 与标量 1 相乘,结果仍然是矩阵 A:1A = A
-
零元:将矩阵 A 与标量 0 相乘,结果是一个零矩阵,其所有元素都是 0:0A = 0,其中 0 是与 A 同样大小的零矩阵。
-
逆元:如果标量 c 不是零,那么存在一个逆标量 1/c,使得以下等式成立:c(A/c) = A 这意味着将矩阵 A 与 c 相乘,然后再与 1/c 相乘,会得到原始矩阵 A。
-
乘法的标量倍:如果将矩阵 A 与标量 c 相乘,那么矩阵的每个元素都会被 c 倍:cA[i][j] = c * A[i][j],其中 A[i][j] 是矩阵 A 的第 i 行第 j 列的元素。
-
保持矩阵结构:数乘不会改变矩阵的行数和列数,即数乘前后矩阵的维度保持不变。
埃式筛法找出素数
埃拉托斯特尼筛法核心思想是通过反复标记出合数,从而留下素数。伪代码如下:
输入:最大值 N
1. 创建布尔数组 isPrime[0..N],并将其初始化为 true
2. 设置 isPrime[0] = false 和 isPrime[1] = false (0 和 1 不是素数)
3. 对于每个 i 从 2 到 sqrt(N),执行以下操作:
- 如果 isPrime[i] 为 true(i 是素数):
- 对于所有 j 从 i*i 到 N,以步长 i 更新 isPrime[j] = false(标记 i 的倍数为合数)
4. 输出所有 isPrime[i] 为 true 的 i
代码模板:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
void sieve(int N) {
// 创建布尔数组,并初始化所有数字为 true
vector<bool> isPrime(N + 1, true);
isPrime[0] = isPrime[1] = false; // 0 和 1 不是素数
// 从 2 开始遍历到 sqrt(N)
for (int i = 2; i <= sqrt(N); ++i) {
if (isPrime[i]) { // 如果 i 是素数
// 标记 i 的所有倍数为合数
for (int j = i * i; j <= N; j += i) {
isPrime[j] = false;
}
}
}
// 输出所有素数
for (int i = 2; i <= N; ++i) {
if (isPrime[i]) {
cout << i << " ";
}
}
cout << endl;
}
int main() {
int N;
cout << "请输入最大值 N:";
cin >> N;
cout << "从 1 到 " << N << " 的素数是:";
sieve(N);
return 0;
}
常用运算符
1. pow
函数原型:
double pow(double base, double exponent);
base
:底数,可以是任意类型的数(通常是double
类型)。exponent
:指数,也可以是任意类型的数,通常是double
类型。
返回值:
- 返回
base
的exponent
次方,即。
- 返回类型为
double
。
示例:
double result = pow(base, exponent);
2. sin(n)求正弦,n为弧度
3. cos(n)求余弦,n为弧度
常见错误
运行时栈溢出
1. 当命令行执行不下去,或出现如下报错,可能是开了动态数组,导致运行时内存空间不足。
根据数据量改动态数组为静态数组就能解决这个问题。
int n;
cin>>n;
int v[n]={0};// 动态数组,大小由变量n决定
//改进:
int v[100000]={0}// 先设置静态数组,大小为100000
2. 当返回错误,出现如下报错,可能是在main()中定义非常大的数组时,由于栈空间有限,导致栈溢出,从而引发程序崩溃。
#include <iostream>
using namespace std;
int main() {
int largeArray[100000000]; // 需要较大的栈空间
return 0;
}
解决方案1:在main()函数外部(即在全局作用域或静态作用域内)定义一个大数组,数组会分配在 数据段(或 BSS 段)中,而不是栈上。数据段的空间远大于栈空间。
#include <iostream>
using namespace std;
int largeArray[100000000]; // 全局数组,分配在数据段
int main() {
cout << "Array size: " << sizeof(largeArray) / sizeof(largeArray[0]) << endl;
return 0;
}
解决方案2:
堆内存是用于动态内存分配的,通过new或malloc等手段分配,内存大小通常受系统限制较大,不像栈那样有固定大小。你可以在堆上分配非常大的数组而不容易发生栈溢出。
#include <iostream>
using namespace std;
int main() {
int* largeArray = new int[100000000]; // 在堆上分配内存
cout << "Array size: " << 100000000 << endl;
// 使用完后记得释放内存
delete[] largeArray;
return 0;
}
输入和输出
int,float,double等数据类型长度及范围整理
参考:c++中 int, long long, double 等数据类型的长度及范围整理_long long double-CSDN博客
getline()的使用
可以使用getline()函数读入整行:getline(cin,s)会从标准输入读入字符,并存入字符串string s中,直到换行符或文件末尾为止。getline()读取一行输入时,会读取到换行符(\n),但是会丢弃换行符,不会将换行符存储在目标字符串中。
然而,如果在getline()之前使用了其他输入方法(比如cin>>),该输入方法也会读取数据并留下一个换行符在缓冲区中,这可能会导致下一次调用getline()时立即读取到空行或出错。遇到这种情况可以使用cin.ignore()函数或getchar()函数解决。
1. cin.ignore()
cin.ignore()会丢弃输入缓冲区中的下一个字符,通常用于忽略换行符,以确保getline()正常工作。你可以指定丢弃的字符数,通常是丢弃 1 个字符(换行符),但如果缓冲区中有多个多余的字符,也可以指定更大的数量,比如cin.ignore(10000,'\n')。示例:
2. getchar()
getchar()是一个 C 风格的输入函数,通常用于从标准输入中读取一个字符,并返回该字符。
如果你调用getchar(),它会从输入缓冲区读取下一个字符,无论是空格、换行符还是其他字符,直到你读取到一个字符为止。
#include <iostream>
#include <string>
using namespace std;
int main() {
int num;
string line;
// 输入一个整数
cout << "Enter a number: ";
cin >> num;
// 在输入整数后,cin缓冲区中可能还留有一个换行符
// 这会影响后续的getline读取正确的数据
// 在读取string之前,忽略缓冲区中的换行符
cin.ignore(); // 这会丢弃换行符
// 使用 getchar() 丢弃输入缓冲区中的换行符
//getchar(); // 读取并丢弃换行符
// 输入一行字符串
cout << "Enter a line of text: ";
getline(cin, line); // 现在可以正确读取用户输入
// 输出结果
cout << "You entered: " << line << endl;
return 0;
}
round()+printf实现四舍五入控制位数的输出
C++11 以后,round可以用来对浮动数进行四舍五入。例如,你可以通过乘以 1000、四舍五入后再除以 1000 来保留三位小数。
但是它本身并不会控制输出格式,也就是说,它只会对数值本身进行四舍五入,但不会强制要求输出三位小数。你可以使用printf来控制输出的浮动数值并保留三位小数。要输出三位小数,你需要在格式说明符%f中指定精度。例如,使用%.3f来输出保留三位小数。
示例:
#include <iostream>
#include <cstdio> // 包含 printf
#include <cmath> // 包含 std::round
using namespace std;
int main() {
double num = 3.1415926535;
// 四舍五入到三位小数
double rounded = std::round(num * 1000.0) / 1000.0;
// 使用 printf 输出三位小数
printf("%.3f\n", rounded);
return 0;
}
setprecision、fixed控制输出形式
参考:【C++】关于fixed和setprecision的学习和介绍_c++_cat_with_cat-GitCode 开源社区
在 C++ 中,setprecision和fixed是用于控制输出流格式的控制符。它们通常与iomanip头文件一起使用,可以帮助你格式化浮点数的输出,指定显示的精度和格式。只控制输出格式,而不需要修改实际的浮动数值。
1. setprecision单独使用
setprecision指定输出的有效数字,以四舍五入进行保留,具体来说:
- 如果没有使用fixed或scientific格式,setprecision控制的是输出的总数字位数(包括整数部分+小数部分)。
- 如果使用了fixed或scientific格式,setprecision控制的是小数部分的 位数。
2. fixed 和setprecision一起使用
fixed 控制浮点数的输出格式,确保浮点数以 定点形式(即小数点后固定的位数)进行输出。它和 setprecision配合使用时,可以更精确地控制小数部分的位数。
示例:
#include <iostream>
#include <iomanip> // 需要包含这个头文件
using namespace std;
int main() {
double pi = 3.14159265358979;
// 默认精度:输出6位有效数字
cout << "Default precision: " << pi << endl;
// 设置精度为10,输出10位有效数字
cout << setprecision(10);
cout << "Precision 10: " << pi << endl;
// 设置精度为4,默认模式下是有效数字
cout << fixed << setprecision(4);
cout << "Fixed with precision 4: " << pi << endl;
return 0;
}
注意:<<fixed<<setprecision(n)并不会改变数据本身。若数据本身为整数型,则<<fixed<<setprecision(n)会最终输出整数型。
如果你希望解除fixed对后续输出的控制,可以使用defaultfooat控制符。defaultfooat会恢复默认的浮点数输出格式,不再强制使用定点格式。但有些编译器不支持 defaultfooat(比如dev c++),这时候就需再写一句<<fixed<<setprecision(n)手动修改输出格式。
示例:
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double pi = 3.14159265358979;
// 使用 fixed 设置定点格式,并设置精度
cout << fixed << setprecision(4);
cout << "Fixed format: " << pi << endl;
// 重新设置 setprecision 来调整输出精度
cout << setprecision(6);
cout << "Adjusted precision: " << pi << endl;
return 0;
}
//输出:
//Fixed format: 3.1416
//Adjusted precision: 3.14159
前缀和
示例:【CSP CCF记录】202012-2 期末预测之最佳阈值_ccf csp期末成绩之最佳预测-CSDN博客
字符串
常见操作
1. 在已有字符串后添加字符或字符串
(1)push_back添加单个字符
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello";
str.push_back(' '); // 添加一个空格字符
str.push_back('W'); // 添加字符 'W'
str.push_back('o'); // 添加字符 'o'
str.push_back('r'); // 添加字符 'r'
str.push_back('l'); // 添加字符 'l'
str.push_back('d'); // 添加字符 'd'
cout << str << endl; // 输出 "Hello World"
return 0;
}
(2) append()函数添加单个字符或字符串
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello";
str.append(1, '!'); // 向字符串末尾添加一个字符 '!',添加n个字符“1”改为“n”
cout << str << endl; // 输出 "Hello!"
return 0;
}
#include <iostream>
#include <string>
using namespace std;
int main() {
string str1 = "Hello";
string str2;
cin>>str2;
//string str2 = " World";
// 将 str2 接到 str1 后面
str1.append(str2);
std::cout << str1 << std::endl; // 输出 "Hello World"
return 0;
}
(3) 直接使用+运算符添加字符或字符串
#include <iostream>
#include <string>
using namespace std;
int main() {
string str1 = "Hello";
string str2 = " World";
// 将 str2 接到 str1 后面
str1 += str2;
// str1+="World";
// str1+="W";后面添加单个字符
cout << str1 << endl; // 输出 "Hello World"
return 0;
}
容器Vector
初始化
以下全部使用int举例
1. 定义空向量
vector<int> a; //相当于空数组
2.定义具有10个整型元素的向量
vector<int> a(10); //相当于a[10]
3.定义具有10个整型元素的向量,且赋予每个元素初值为1
vector<int> a(5,1); //相当于int a[5] = {1,1,1,1,1}
注意:
int a[5] = {1};//相当于 int a[5] = {1,0,0,0,0}
int a[5] = {0};//相当于 int a[5] = {0,0,0,0,0}
4.定义与向量b具有相同值的向量a
vector<int> a(b); //将向量b赋值给向量a,即向量a等于向量b
5.将向量b中下标0-1(共三个)的元素赋值给a
//第一个数据是起始地址,第二个数据是结束地址(不包括),第二个数据就是你要截取的长度加1
vector<int> a(b.begin(), b.begin()+3);
6.从数组中获得初值
int b[7] = {1,2,3,4,5,6,7}; //定义整形数组
vector<int> a(b, b+7); //将数组b赋值给a,第一个数据是起始地址,第二个数据是结束地址(不包括)
7. 二维数组初始化
vector<vector<int>> a; //初始化为int型二维数组,相当于int a[][]
assign()函数
可对已定义好的vector向量进行赋值
//将b的下标为0-2的元素赋值给向量a
a.assign(b.begin(), b.begin()+3);
//使向量a变为长度为4且值为2
a.assign(4,2);
back()函数
a.back(); //返回a的最后一个元素
front()函数
a.front(); //返回a的第一个元素
find()+distance()函数查找元素位置
find()它返回的是一个迭代器,指向查找到的元素。如果找到元素,可以通过distance()计算该迭代器与容器起始位置的距离来获得元素的索引位置。
auto it=find(a.begin(), a.end(), stu);
pos =distance(a.begin(), it);
insert()函数
//在a向量第二个元素(下标从0开始)后插入 8
a.insert(a.begin()+2, 8);
//在a向量的第二个元素(下标从0开始)后插入3个数,其值都为8
a.insert(a.begin()+1, 3, 8);
//b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin()+1, b+3, b+6);
示例:
void fun(){
a.clear(); //清空a
for(int i = 1; i <= 5; i++) a.push_back(i); //对a输入数据
b.assign(10, 6); //对b输入数据
}
int main(){
fun();
cout << "使用insert插入前:" ;
for(int i = 0; i < a.size(); i++){
cout << a[i] << " ";
}
cout << endl;
a.insert(a.begin() + 2, 8);
cout << "使用insert插入后:" ;
for(int i = 0; i < a.size(); i++) cout << a[i] << " ";
cout << endl << endl;
fun();
cout << "使用insert插入前:" ;
for(int i = 0; i < a.size(); i++){
cout << a[i] << " ";
}
cout << endl;
a.insert(a.begin() + 2, 3, 8);
cout << "使用insert插入后:" ;
for(int i = 0; i < a.size(); i++) cout << a[i] << " ";
cout << endl << endl;
fun();
cout << "使用insert插入前:" ;
for(int i = 0; i < a.size(); i++){
cout << a[i] << " ";
}
cout << endl;
a.insert(a.begin() + 2, b.begin()+3, b.begin()+6);
cout << "使用insert插入后:" ;
for(int i = 0; i < a.size(); i++) cout << a[i] << " ";
cout << endl;
return 0;
}
//输出:
使用insert插入前:1 2 3 4 5
使用insert插入后:1 2 8 3 4 5
使用insert插入前:1 2 3 4 5
使用insert插入后:1 2 8 8 8 3 4 5
使用insert插入前:1 2 3 4 5
使用insert插入后:1 2 6 6 6 3 4 5
返回值为vector的函数
#include <iostream>
#include <vector>
#include <utility> // for std::move
using namespace std;
vector<int> createVector() {
vector<int> vec = {1, 2, 3, 4, 5}; // 创建并初始化一个 vector
return std::move(vec); // 使用 move 语义返回
}
int main() {
vector<int> myVector = createVector(); // 获取返回的 vector
// 输出返回的 vector
for (int num : myVector) {
cout << num << " ";
}
cout << endl;
return 0;
}
容器map
参考:C++中的map用法详解_c++ map-CSDN博客
map是一个存储键值对(key-value pairs)的容器,数据类型自己定义。map是有序的,即元素会按照键(key)进行自动排序。map的内部结构是红黑树,它能够在对元素进行插入、查找和删除操作时提供 对数时间复杂度(O(log n))。
定义map类型
#include<bits/stdc++.h>
using namespace std;
int main() {
// 创建一个 map,键为 int 类型,值为 string 类型
map<int, string> m;
// 插入元素
m[1] = "Apple";
m[2] = "Banana";
m[3] = "Cherry";
// 另一种插入方式,使用 insert 函数
m.insert(make_pair(4, "Date"));
//遍历输出
//第一种写法
for(auto i=m.begin();i!=m.end();i++){
cout<<"键="<<i->first<<" 值="<<i->second<<endl;
}
//第二种写法
map<string,int>::iterator it;
for(it=m.begin();it!=m.end();it++){
cout<<"键="<<it->first<<" 值="<<it->second<<endl;
}
return 0;
}
添加元素
每次添加一个新的键,值默认为0。
map<string,int>m;//定义m
1:使用insert添加元素
m.insert(pair<string,int>("sd",19));
2:直接使用数组下标
m["sd"]=19;
模板
#include<bits/stdc++.h>
using namespace std;
map<string,int>m;
int n;
int main()
{
cout<<"请输入要添加的元素个数:";
cin>>n;
for(int i=0;i<n;i++){
string s;
int id;
cout<<"键:";
cin>>s;
cout<<"值:";
cin>>id;
m.insert(pair<string,int>(s,id));//insert添加元素
//m[s]=id;//数组添加
}
//遍历输出
map<string,int>::iterator it;
for(it=m.begin();it!=m.end();it++){
cout<<"键="<<it->first<<" 值="<<it->second<<endl;
}
return 0;
}
查找元素find()/count()
(1) 使用find()查找元素,返回一个迭代器指向元素。如果没有找到,返回end()。
#include<bits/stdc++.h>
using namespace std;
int main()
{
// 插入元素
map<int, std::string> m;
m[1] = "Apple";
m[2] = "Banana";
m[3] = "Cherry";
auto it = m.find(3); // 查找键为 3 的元素
if (it != m.end()) {
cout << "Found: " << it->first << " -> " << it->second << endl;
}else {
cout << "Key not found!" << endl;
}
return 0;
}
(2)使用count查找元素。count函数的意思就是查找这个键的出现次数,map中键是唯一的,所以它的值要么是0,
要么是1,是1不就是查找成功吗,它的缺点是可以确定是否存在这个键,可是却不能确定这个键的位置。
#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
// 插入元素
map<int, std::string> m;
m[1] = "Apple";
m[2] = "Banana";
m[3] = "Cherry";
int it = m.count(3); // 查找键为 3 的元素
if (it == 1){
cout << "Found it!" << endl;
}else {
cout << "Key not found!" << endl;
}
return 0;
}
删除元素erase()
erase()函数,参数可以是键或迭代器。
m.erase(2); // 删除键为 2 的元素
访问元素
operator[]或at()函数,通过键访问元素。
cout << m[1] << endl; // 使用 operator[] 访问
cout << m.at(2) << endl; // 使用 at() 访问
容器unordered_map
C++中map容器和unordered_map容器的主要异同。
常用函数
erase()函数
详细解释:C++中的erase()函数用法总结-CSDN博客
erase可以删去容器中指定位置的元素,容器的size(大小)会改变,但是容器的容量不变。常用在字符串和vector容器中。
earse时间复杂度是 O(n),其中n是删除元素后面的元素个数。这是因为删除元素后,容器中所有位于删除位置之后的元素都会向前移动。
常见用法:
1. 删除指定位置的单个元素
str.erase(pos,1)//删除字符串pos位置的单个字符,1为字符个数
vec.erase(vec.begin()+pos)//删除vector容器中pos位置的单个字符
//vector容器中,erase()允许通过迭代器来删除元素
auto it = find(vec.begin(), vec.end(), 30);
if (it != vec.end()) {
// 使用 erase 删除指向元素 30 的迭代器
vec.erase(it);
}
2. 删除指定范围内的元素
str.erase(firstpos, n);//字符串,从位置firstpos开始,删除n个字符
vec.erase(vec.begin() + firstpos, vec.begin() + lastpos);//删除指定范围为 [first, last)
vec.erase(first, last)//删除从 first 到 last 之间的元素,first、last为迭代器
注意:在字符串中str.erase(pos)是删除str[pos]及之后所有字符元素,并不是只删除str[pos]。
示例:
在字符串中使用erase()函数
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello, World!";
//1. 删除指定位置的单个字符
str.erase(5, 1);// 删除索引为 5 的字符(即 ',')
cout <<"erase(pos,1):"<< str << endl; // 输出:Hello World!
//2. 删除第一个出现的字符 'o'
string str1 = "Hello, World!";
size_t pos = str.find('o');
if (pos != string::npos) {
str1.erase(pos, 1); // 删除找到的字符
}
cout <<"erase('o'pos,1):"<< str1 << endl; // 输出:Hell, World!
//3. 删除指定位置到结尾的所有字符
string str2 = "Hello, World!";
str2.erase(5); // 从索引 5 开始,删除所有字符
cout <<"erase(pos):"<< str2 << endl; // 输出:Hello
//4. 删除指定范围字符
string str3 = "Hello, World!";
str3.erase(0, 5); // 从位置 0 开始,删除 5 个字符
cout <<"erase(pos,n):"<< str3<< endl; // 输出:, World!
//5. 删除整个字符串字符
string str4 = "Hello, World!";
str4.erase(0, str4.length());// 删除从第 0 个字符开始的所有字符
cout <<"erase(0, str.length():"<< str4 << endl; // 输出:空字符串
return 0;
}
在vector容器中使用erase()函数
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
//1. 删除指定位置的单个元素
vector<int> vec = {1, 2, 3, 4, 5};
vec.erase(vec.begin() + 2);// 删除位置为 2 的元素(即 3)
cout << "erase(vec.begin()+pos):";
for (int val : vec) {
cout<<val << " "; // 输出:1 2 4 5
}
cout<<endl;
//2. 删除指定范围的元素
vector<int> vec1 = {1, 2, 3, 4, 5};
vec1.erase(vec1.begin() + 1, vec1.begin() + 4);// 删除索引 1 到 3 范围的元素(即 2, 3, 4)
cout << "erase[1,4):";
for (int val : vec1) {
cout<<val << " "; // 输出:1 5
}
cout<<endl;
//3. 删除所有元素
vector<int> vec2 = {1, 2, 3, 4, 5};
vec2.erase(vec2.begin(), vec2.end());
cout << "Size of vector: " << vec2.size() << endl; // 输出:Size of vector: 0
return 0;
}
sort()函数
sort函数是用于排序容器中元素的标准算法。它位于<algorithm>
头文件中,可以对数组、vector、list等容器进行排序。
1. 对数组进行排序
int arr[] = {10, 2, 4, 5, 1};
// 默认对数组 arr 进行升序排序
sort(arr, arr + 5);
自定义比较函数实现升序排序和降序排序
static bool cmp1(int &lhs,int &rhs)//升序
{
return lhs<rhs;
}
static bool cmp2(int &lhs,int &rhs)//降序
{
return lhs>rhs;
}
sort(arr,arr+5,cmp1);//升序
sort(arr,arr+5,cmp1);//降序
除了自定义比较函数,还可以使用greater和less实现降序、升序排序。
sort(arr,arr+10,less<int>());//从小到大排序
sort(arr,arr+10,greater<int>());//从大到小排序
2. 对vector排序
vector<int> vec = {10, 2, 4, 5, 1};
// 对 vector 进行升序排序
sort(vec.begin(), vec.end());
自定义比较函数实现升序排序和降序排序
sort(vec.begin(),vec.end(),cmp1);
sort(vec.begin(),vec.end(),cmp2);
除了自定义比较函数,还可以使用greater和less实现降序、升序排序。
sort(vec.begin(), vec.end(),less<int>());//从小到大排序
sort(vec.begin(), vec.end(),greater<int>());//从大到小排序
3. 时间复杂度
sort通常基于 快速排序(QuickSort)或者 堆排序(HeapSort)等算法,时间复杂度为 O(n log n)。在最坏情况下,它的复杂度为 O(n²),例如使用快速排序时,如果数据已经近乎有序,性能可能会受到影响。
减小运行时间
使用结构体代替多个数组
理论上说,定义多个数组可能会导致运行时间过长的原因通常涉及到 内存访问效率 和 数据局部性 的问题。而使用 结构体 替代多个数组,可以通过改善数据的布局和内存访问模式来提高程序的性能,减少运行时间。
假设你在代码中定义了多个数组,类似于以下的情况:
int x[1000];
int y[1000];
int z[1000];
如果这些数组彼此之间的元素是相关联的,比如三维空间中的一个点,就可以使用结构体可以将相关的数据放到一个内存结构中,从而减少内存分散,提高内存访问效率。
struct Point {
int x;
int y;
int z;
};
Point points[1000];
使用排序+for循环代替两层for循环
例如:【CSP CCF记录】201712-1第12次认证 最小差值-CSDN博客
在一个无序数组中,找出元素间的最小差值。
若使用两层for循环,依次计算每个元素与其他所有元素的差值,时间复杂度为
若先使用sort()函数对无序数组进行排序,再for循环依次计算每个元素与其相邻元素的差值,时间复杂度就是sort()函数的
因此,先排序再for循环可以在处理大量数据时提高运行效率。
考虑周期问题
示例:【CSP CCF记录】202409-2第35次认证 字符串变换-CSDN博客
在字符变换问题中,如果执行每一次替换,可能会导致运行时间过长。
考虑到字符可能有变换周期,例如以下这种情况,字符最终又会变回自身:
F(a)=b,F(b)=c,F(c)=d,F(d)=a。
那么我们可以计算字符变换的周期,在main()函数里只要找到字符正在经历最新周期里的哪一次变换,这样可以大大减少运行时间。
可能用到以下代码,
int x=times%sz;
- x:最新周期的第x次变换
- times:总共变换的次数
- sz:一个周期内变换的次数