我的C++笔记
数的取整、舍入
ceil()函数:向上取整,返回不小于参数的最小整数(浮点型)。
例如:
#include <cmath>
double x = 3.2;
double y = -2.7;
std::cout << ceil(x) << " "; // 输出 4.0
std::cout << ceil(y); // 输出 -2.0(注意负数的方向)floor()函数:向下取整,返回不大于参数的最大整数(浮点型)。
例如:
#include <cmath>
std::cout << floor(3.7); // 输出 3.0
std::cout << floor(-2.3); // 输出 -3.0trunc()函数:截断小数部分,返回参数的整数部分(浮点型)。
例如:
#include <cmath>
std::cout << trunc(3.7); // 输出 3.0
std::cout << trunc(-2.7); // 输出 -2.0round()函数:四舍五入到最接近的整数,中间值(如 0.5)向偶数方向取整(遵循 IEEE 754)。
例如:
#include <cmath>
std::cout << round(2.3); // 输出 2.0
std::cout << round(2.5); // 输出 2.0(偶数侧)
std::cout << round(3.5); // 输出 4.0(偶数侧)其他的数学库函数
abs()函数:在<cstdio>中定义,用于整型(返回整型)。
fabs()函数:在<cmath>中定义,用于浮点型(返回浮点型)。
例如:
#include <cstdio>
#include <cmath>
std::cout << abs(-5); // 输出 5
std::cout << fabs(-3.5); // 输出 3.5pow()函数:幂运算
例如:
#include <cmath>
std::cout << pow(2, 3); // 输出 8.0
std::cout << pow(3, 0.5); // 输出 1.732(√3 的近似值)sqrt()函数:平方根
例如:
#include <cmath>
std::cout << sqrt(16.0); // 输出 4.0modf()函数:将参数拆分为整数部分和小数部分,返回小数部分,整数部分通过指针参数存储。
例如:
#include <cmath>
double x = 3.14;
double int_part;
double frac_part = modf(x, &int_part);
std::cout << int_part << " " << frac_part; // 输出 3.0 0.14gcd()函数:求两个数的最大公约数。注:会与需要约分的分数或约分输出相关。
例如:
#include <numeric>
#include <iostream>
int main() {
int a = 12, b = 18;
std::cout << std::gcd(a, b); // 输出 6
return 0;
}一些算法
排序算法
冒泡排序
优点:
简单易懂:算法逻辑简单,容易理解和实现
稳定排序:相等元素的相对位置保持不变
原地排序:不需要额外的存储空间
适应性:优化版本对已排序或基本有序的数组效率较高
缺点:
效率低:时间复杂度为O(n²),不适合大数据集
交换频繁:需要进行大量的元素交换操作
例如:
void safeBubbleSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) { //此处为升序,需降序只需改为<
swap(arr[j], arr[j + 1]);
}
}
}
}快速排序
例如:
#include<iostream>
#include<vector>
using namespace std;
int midval_sort(vector<int> & num,int low,int high)
{
int mid=low+(low+high)/2;
if (num[low]>num[mid]){
swap(num[mid],num[low]);
}
if (num[low]>num[high]){
swap(num[low],num[high]);
}
if (num[mid]>num[high]){
swap(num[mid],num[high]);
}
int i=low-1;
for (int j=low;j<high;j++)
{
if (num[j]<=num[high]){
i++;
swap(num[j],num[i]);
}
}
swap(num[i+1],num[high]);
return i+1;
}
void quicksort(vector<int> & num,int low,int high)
{
if (low<high){
int mid=midval_sort(num,low,high);
quicksort(num,low,mid-1);
quicksort(num,mid+1,high);
}
}关于数组的处理
多维变长数组(含有变量的数组)的声明及初始化方式
因C++标准不允许在栈上声明变长数组,因此使用vector,即在堆上声明来实现
例如:
#include <vector>
using namespace std;
// 声明并初始化一个全零的三维数组
vector<vector<vector<int>>> cube(
w, // 第一维大小
vector<vector<int>>( // 第二维初始化方式
x, // 第二维大小
vector<int>(h, 0) // 第三维初始化方式:h 个 0
)
);妙用求模来模拟圆圈列表
例如:
#include<iostream>
#include<vector>
#include<string>
#include<cmath>
using namespace std;
struct people{
string name;
int pos;
int sw;
};
int main()
{
int n,m;
cin>>n>>m;
vector<people> list(n);
for (int i=0;i<=n-1;i++)
{
cin>>list[i].sw>>list[i].name;
list[i].pos=i;
}
people next=list[0];
for (int i=1;i<=m;i++)
{
int to_sw,num;int next_pos;
cin>>to_sw>>num;
if (next.sw==0&&to_sw==0||next.sw==1&&to_sw==1){
next_pos=((next.pos-num%n)+n)%n;
}
else{
next_pos=(next.pos+num)%n;
}
next=list[next_pos];
}
cout<<next.name;
return 0;
}使用双循环统计周围地雷数
例如:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
vector<string> str(a);
vector<string> out(a,string(b,' '));
for (int i=0;i<=a-1;i++)
{
cin>>str[i];
}
for (int i=0;i<=a-1;i++)
{
for (int j=0;j<=b-1;j++)
{
if (str[i][j]=='*'){
out[i][j]='*';
}
else{
int count=0;
for (int adx=-1;adx<=1;adx++)
{
for (int ady=-1;ady<=1;ady++)
{
if (adx==0&&ady==0){
continue;
}
int x=j+adx;
int y=i+ady;
if (x>=0&&x<=b-1&&y>=0&&y<=a-1){
if (str[y][x]=='*'){
count++;
}
}
}
}
out[i][j]='0'+count;
}
}
}
for (string & i:out)
{
cout<<i<<endl;
}
return 0;
}将二维字符数组旋转90度
例如:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
vector<string> theOne(vector<string> start)
{
int n=start.size();
vector<string> rol(n,string(n,' '));
for (int i=0;i<=n-1;i++)
{
for (int j=0;j<=n-1;j++)
{
rol[j][n-1-i]=start[i][j]; //这里是主要的交换方式
}
}
return rol;
}二进制运算
使用二进制运算来进行快速幂运算
原理:
对于计算 a^b,我们可以将指数b表示为二进制形式,然后利用幂的乘法性质:
a^b = a^(b0×2^0 + b1×2^1 + ... + bk×2^k) = ∏ a^(bi×2^i)
例如:
#include <iostream>
using namespace std;
// 快速幂算法
long long fastPower(long long base, long long exponent) {
long long result = 1;
while (exponent > 0) {
// 如果当前位是1,将对应的幂乘到结果中
if (exponent & 1) {
result *= base;
}
// 底数平方,对应二进制位的移动
base *= base;
// 指数右移一位,相当于除以2
exponent >>= 1;
}
return result;
}
int main() {
long long a = 2, b = 10;
cout << a << "^" << b << " = " << fastPower(a, b) << endl;
// 输出: 2^10 = 1024
a = 3, b = 5;
cout << a << "^" << b << " = " << fastPower(a, b) << endl;
// 输出: 3^5 = 243
return 0;
}如果运算3^13,这是运行过程:
指数 13 的二进制: 1101
计算过程:
result = 1, base = 3, exponent = 13 (1101)
第1位(最低位): 1 -> result *= base → result = 3
base = base² = 9
exponent >>= 1 → 6 (110)
第2位: 0 -> 不操作
base = 9² = 81
exponent >>= 1 → 3 (11)
第3位: 1 -> result *= base → result = 3 × 81 = 243
base = 81² = 6561
exponent >>= 1 → 1 (1)
第4位: 1 -> result *= base → result = 243 × 6561 = 1594323
exponent >>= 1 → 0
最终结果: 1594323使用二进制运算来解决子集相加问题
例如:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> num;
int x;
while (cin>>x)
{
num.push_back(x);
}
int n=num.size();
int sum=0;
for (int mask=1;mask<=(1<<n)-1;mask++)
{
for (int i=0;i<=n-1;i++)
{
if (mask&(1<<i)){
sum+=num[i];
}
}
}
cout<<sum;
return 0;
}string类型
对于string类型的插入
对于插入:
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello World";
// 1. 在指定位置插入字符串
str.insert(5, " Beautiful"); // 在位置5插入
cout << str << endl; // 输出: Hello Beautiful World
// 2. 在开头插入
str.insert(0, "Start: ");
cout << str << endl; // 输出: Start: Hello Beautiful World
// 3. 在末尾插入(两种方式)
str.insert(str.length(), " End"); // 方式1
str.append("!!!"); // 方式2(更简洁)
cout << str << endl; // 输出: Start: Hello Beautiful World End!!!
return 0;
}对于string类型的截断
对于截断:
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello Beautiful World";
// 1. 截取从位置6开始的子串(到末尾)
string part1 = str.substr(6);
cout << "从位置6开始: " << part1 << endl; // 输出: Beautiful World
// 2. 截取从位置6开始的5个字符
string part2 = str.substr(6, 5);
cout << "从位置6取5个字符: " << part2 << endl; // 输出: Beaut
// 3. 截取最后5个字符
string part3 = str.substr(str.length() - 5);
cout << "最后5个字符: " << part3 << endl; // 输出: World
return 0;
}对于string类型的查找
对于查找:
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "Hello World, Welcome to C++ Programming";
// 1. 查找子字符串第一次出现的位置
size_t pos1 = str.find("World");
if (pos1 != string::npos) {
cout << "'World' 在位置: " << pos1 << endl; // 输出: 6
} else {
cout << "未找到" << endl;
}
// 2. 查找字符第一次出现的位置
size_t pos2 = str.find('W');
cout << "'W' 在位置: " << pos2 << endl; // 输出: 6
// 3. 从指定位置开始查找
size_t pos3 = str.find('o', 5); // 从位置5开始找'o'
cout << "从位置5开始找'o': " << pos3 << endl; // 输出: 7
// 4. 查找最后一次出现的位置
size_t pos4 = str.rfind('o');
cout << "'o' 最后一次出现在: " << pos4 << endl; // 输出: 25
return 0;
}使用transform()来转化字符为大小写
例如:
#include <iostream>
#include <algorithm>
#include <string>
#include <cctype>
using namespace std;
int main() {
string str = "Hello World 123!";
// 转换为小写
transform(str.begin(), str.end(), str.begin(), ::tolower);
cout << "小写: " << str << endl;
// 转换为大写
transform(str.begin(), str.end(), str.begin(), ::toupper);
cout << "大写: " << str << endl;
return 0;
}单行不同类型的数据输入:使用getline与stringstream
#include<string>
#include<vector>
#include<sstream>
using namespace std;
int main()
{
int n;
cin>>n;
cin.ignore(); //使用ignore()将上一行遗留下的回车舍去,避免被getline的读取
string str;
for (int i=1;i<=n;i++)
{
string line;
getline(cin,line);
stringstream ss(line); //将line中的字符串压入stringstream类型的ss中
vector<string> tokens;
string token;
while (ss>>token) //将ss中的以空格分开的字符串分别压入token中
{
tokens.push_back(token);
}
if (tokens.size()==3){ //当输入一共有三种类型时
str=tokens[0];
int a,b;
a=stoi(tokens[1]);
b=stoi(tokens[2]);
}
else{
int a,b;
a=stoi(tokens[0]);
b=stoi(tokens[1]);
}
}
}判断一个范围:超过范围的取边界,否则取最大(最小)值
例如:
// 处理 5x5 区域
for (int i = max(x-2, 0); i <= min(x+2, n-1); i++) { // X 轴方向遍历(列):
// 起始位置为x坐标向左2格,但不小于0
// 终止位置为x坐标向右2格,但不大于n-1
for (int j = max(y-2, 0); j <= min(y+2, n-1); j++) { // Y 轴方向遍历(行):
// 起始位置为y坐标向上2格,但不小于0
// 终止位置为y坐标向下2格,但不大于n-1
num[j][i] = 1; // 当前坐标为 (i,j),将二维数组该位置标记为1
// 注意:因数组是 num[row][col] 结构(先行后列)
// 所以j对应纵坐标(行),i对应横坐标(列)
}
}
高精度算法
高精度加法:用手动模拟加法的方式来计算
例如:
#include<iostream>
#include<vector>
using namespace std;
vector<int> add(vector<int> a,vector<int> b)
{
vector<int> out;
int temp =0;
for (int i=0;i<a.size()||i<b.size()||temp;i++)
{
int a_val = (i<a.size())?a[i]:0;
int b_val = (i<b.size())?b[i]:0;
int sum=a_val+b_val+temp;
out.push_back(sum%10);
temp = sum/10;
}
return out;
}高精度乘法(高精度数乘低精度数):用手动模拟乘法的方式来计算
例如:
#include<iostream>
#include<vector>
using namespace std;
vector<int> multiply(vector<int> a,int b)
{
vector<int> out;
int temp=0;
for (int i=0;i<a.size()||temp;i++)
{
int temp_2 =temp;
if (i<a.size()){
temp_2 += a[i]*b;
}
out.push_back(temp_2%10);
temp = temp_2/10;
}
return out;
}高精度乘法(高精度数乘高精度数):用手动模拟乘法的方式来计算
例如:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
void rev(vector<int> & num)
{
vector<int> temp;
for (int i=num.size()-1;i>=0;i--)
{
temp.push_back(num[i]);
}
num=temp;
}
vector<int> multiply(vector<int> & a,vector<int> & b)
{
vector<int> out;
rev(a);
rev(b);
for (int i=0;i<a.size();i++)
{
for (int j=0;j<b.size();j++)
{
int pos=i+j;
if (pos>=out.size()){
out.push_back(0);
}
out[pos]+=a[i]*b[j];
}
}
int temp=0;
for (int i=0;i<out.size()||temp;i++)
{
if (i>=out.size()){
out.push_back(0);
}
out[i]+=temp;
temp=out[i]/10;
out[i]=out[i]%10;
}
rev(out);
if (out[0]==0&&out.size()>1){
out={0};
}
return out;
}
int main()
{
string str1,str2;
cin>>str1>>str2;
vector<int> a,b;
for (int i=0;i<=str1.size()-1;i++)
{
a.push_back(str1[i]-'0');
}
for (int i=0;i<=str2.size()-1;i++)
{
b.push_back(str2[i]-'0');
}
vector<int> out=multiply(a,b);
for (int & i:out)
{
cout<<i;
}
return 0;
}判断质数/素数(简单法)1
例如:
bool isPrime(int n)
{
if (n<=3){
return n>1;
}
if (n%6!=1&&n%6!=5){
return false;
}
int limit=sqrt(n);
for (int i=5;i<=limit;i+=6){
if (n%i==0||n%(i+2)==0){
return false;
}
}
return true;
}判断质数/素数(简单法)2
例如:
using namespace std;
bool prime(int n)
{
if (n==1||n!=2&&n%2==0){
return false;
}
for (int i=3;i*i<=n;i++)
{
if (n%i==0){
return false;
}
}
return true;
}生成回文质数
例如:
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
bool isPrime(int n)
{
if (n<=3){
return n>1;
}
if (n%6!=1&&n%6!=5){
return false;
}
int limit= sqrt(n);
for (int i=5;i<=limit;i+=6)
{
if (n%i==0||n%(i+2)==0){
return false;
}
}
return true;
}
void Palindromes(int a,int b,int lens,string &list,int next=0)
{
if (next>(lens-1)/2){
int num=stoi(list);
if (num>=a&&num<=b&&isPrime(num)){
cout<<num<<endl;
}
return;
}
int start = (next==0)?1:0;
for (int i=start;i<=9;i++)
{
if (start==1&&i%2==0){
continue;
}
list[next] = list[lens-1-next] = i + '0';
Palindromes(a,b,lens,list,next+1);
}
}键值对的实际运用:数字出现的频率统计
例如:
#include <unordered_map> //使用键值对
#include <vector>
#include <climits> // 需要包含此头文件以使用 INT_MAX
using namespace std;
// 输入参数应为数组,此处假设 sum 是存储整数的数组
void exampleFunction(const vector<int>& sum) {
// Step 1: 统计频率(键为数组元素,值为出现次数)
unordered_map<int, int> fq;
for (int i : sum) {
// 若键不存在,unordered_map 会自动插入键并初始化值为0,再自增
fq[i]++;
}
// Step 2: 找到最大的出现次数
int count_max = 0;
for (auto& pair : fq) {
if (pair.second > count_max) {
count_max = pair.second; // 更新最大频率值
}
}
// Step 3: 在出现次数最大的元素中,找到值最小的键
int count_min = INT_MAX; // 初始化为最大整数值,方便取最小值
for (auto& pair : fq) {
// 仅处理出现次数等于最大值的键
if (pair.second == count_max) {
// 比较并记录较小的键
if (pair.first < count_min) {
count_min = pair.first;
}
}
}
// 最终 count_min 即为出现次数最多且值最小的元素
}统计字符的数目:使用unordered_map
例如:
#include<unordered_map>
unordered_map<char,int> count;//声明一个键为char,值为int的unordered_map
for (char & i:str)
{
count[i]++;//统计计数
}
int max=0;
for (auto & x:str)
{
if (str.second>max){
max=str,second;//遍历unordered_map得出统计的最大数
}
}使用unordered_map下的count来确认键值对是否存在
例如:
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<unordered_map>
using namespace std;
int main() {
unordered_map<char, int> count;
vector<string> step1;
// 初始化 A-Z
for (char c = 'A'; c <= 'Z'; c++) {
step1.push_back(string(1, c));
}
string str = "";
string in;
// 读取输入,可以按 Ctrl+Z (Windows) 或 Ctrl+D (Linux/Mac) 结束输入
while (getline(cin, in)) {
str += in;
}
// 统计大写字母出现次数
for (char i : str) {
if (i >= 'A' && i <= 'Z') {
count[i]++;
}
}
// 添加星号
for (int i = 0; i < 26; i++) {
char current_char = 'A' + i;
// 安全访问,如果不存在则次数为0
int char_count = count.count(current_char) ? count[current_char] : 0;
for (int j = 0; j < char_count; j++) {
step1[i] += "*";
}
}sort()函数:用于对数组或支持随机访问迭代器的容器(如vector、deque)进行排序
例如:
#include <algorithm>
#include <vector>
using namespace std;
vector<int> nums = {5, 3, 1, 4, 2};
sort(nums.begin(), nums.end()); // 排序后为 {1, 2, 3, 4, 5}
降序排序的方法:
// 使用标准库提供的 greater<T>
int num[];
sort(nums.begin(), nums.end(), greater<int>()); // {5, 4, 3, 2, 1}需要注意的点
string可以当数组使,使用to_string()可将元素转换为string类型,当遍历string中的元素时,每一个元素都是字符串类型,可以通过-'0'来使字符串转换为数字
对于一个二维数组num[i][j]来说,i为行,j为列
一整个string类型变量是字符串,string[i]是char
当条件太多的时候可以声明一个新的bool变量来简化表达
注意临时变量的重置
碰到超大数据计算不是longlong就是高精度()
当在if中检查数组是否越界时需要把检查条件放在最前面!!(在 C/C++ 中,当使用逻辑与 (&&) 操作符连接多个条件时,如果数组访问的边界检查没有放在数组访问之前,可能会导致段错误(Segmentation Fault))
例如:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> grid = {
"..........",
"..........",
"..........",
"..........",
"..........",
"..........",
"..........",
"..........",
"..........",
".........."
};
int x = 5, y = 0; // y 是 0,尝试向上移动
// 错误写法:先访问数组,再检查边界
if (y - 1 >= 0 && grid[y - 1][x] != '*') {
// 这里不会执行,因为 y-1 >= 0 是 false
}
// 看起来相似,但实际上危险的写法:
if (grid[y - 1][x] != '*' && y - 1 >= 0) {
// 这会导致段错误!
// 因为会先计算 grid[y-1][x] 即 grid[-1][x]
// 这是无效的内存访问
}
return 0;
}
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 onprs的博客
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果