【CSP CCF记录】备考知识点总结

持更中。。。仅用作本人的梳理复习,分享出来希望能帮助更多人

常规注意事项

评测语言及版本的选择

评测结果分析

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的区别

参考i++和++i的区别-CSDN博客

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 类型。

返回值:

  • 返回 baseexponent 次方,即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循环,依次计算每个元素与其他所有元素的差值,时间复杂度为n^{2}

若先使用sort()函数对无序数组进行排序,再for循环依次计算每个元素与其相邻元素的差值,时间复杂度就是sort()函数的n\log n

因此,先排序再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:一个周期内变换的次数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值