数的取整、舍入

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.0

trunc()函数:截断小数部分,返回参数的整数部分(浮点型)。

例如:
#include <cmath>
std::cout << trunc(3.7);  // 输出 3.0
std::cout << trunc(-2.7); // 输出 -2.0

round()函数:四舍五入到最接近的整数,中间值(如 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.5

pow()函数:幂运算

例如:
#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.0

modf()函数:将参数拆分为整数部分和小数部分,返回小数部分,整数部分通过指针参数存储。

例如:
#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.14

gcd()函数:求两个数的最大公约数。注:会与需要约分的分数或约分输出相关。

例如:
#include <numeric>
#include <iostream>
int main() {
    int a = 12, b = 18;
    std::cout << std::gcd(a, b);  // 输出 6
    return 0;
}

一些算法

排序算法

冒泡排序

优点:

  1. 简单易懂:算法逻辑简单,容易理解和实现

  2. 稳定排序:相等元素的相对位置保持不变

  3. 原地排序:不需要额外的存储空间

  4. 适应性:优化版本对已排序或基本有序的数组效率较高

缺点:

  1. 效率低:时间复杂度为O(n²),不适合大数据集

  2. 交换频繁:需要进行大量的元素交换操作

例如:
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;
}