内容基于w3schools中的c++教程

一些很少在CP中遇到的内容(class及一些相关概念)就不看了

C++ Data Types

Scientific Numbers:

double d1=1.14e4 //1.14*10^4
double d2=5.14e-4 //5.14*10^-4

接下来,如何设置输出格式(指定小数位)?

#include <iomanip>
cout<<fixed<<setprecision(2);//小数点后两位

ASCII characters:

只需注意A->65, a->97, 大写字母再前.

C++ string

A string in C++ is actually an object, there are some useful functions:

at(): return an indexed character from a string ,same as str[x]

empty(): checks whether a string is empty or not

substr(pos,len): return a part of string from pos(index) and last for len.

string str1="ciallo,senpai";
cout<<str1.substr(2,3); //"all"

find()/rfind(): return the index of the first/last occurrence of a str or char(if none,return )

size()/length(): 完全等价,看从什么角度看待罢了. 前者容器,后者字符串

Tips:

  • 如何在字符串中保留引号? use backslash. \n&\t也干了.
  • 其它数据类型向string转换:to_string()
  • pop_back()也能用于string.
  • 如何获取包含空格的输入?
string str="joe john"
getline(cin,str);
  • 转化为字符串: to_string()

  • 范围大小写转换(#include<algorithm><cctype>):

    transtorm(str.begin(),str.end(),str.begin(),::toupper);

    第三个参数为迭代器(类指针),transform函数被设计为面向范围操作. STL 的函数设计基于迭代器,使操作可扩展和高效.

    toupper位于全局命名空间(不在std!!),需要显式地加上作用域解析符 ::,避免冲突发生.

C++ Math

most powerful💪 functions:

max()/min():返回最大/小值

sqrt():开根号

round(): 就近取整

<cmath>中的函数列表: clickhere

C++ Switch

嗯目前没怎么用过. 根据表达式的值选择执行的语句,通常和break,default关键字一起使用

enum in C++

如果在程序中用到常量,该怎么定义?

#define(宏定义):全局有效,只是简单的文本替换,不好查错. const:可以在特定的作用域中定义,只在该块范围内有效,避免了全局命名冲突.如果有多个常量,可以用enum(一组常数)封装.

关于为什么叫枚举(enumerations),可能意为”专门列出”,或者…

enum Level{
L, //0
M, //1
H //2 and so on
};

C++ References

A reference variable is a “reference” to an existing variable, which created with the & operator.

现在想想将一个变量传参给函数时,是否新建了一个变量?

引用变量和原始变量共享一个内存地址, 通过引用所做的任何操作直接影响到原始变量.

构造函数(Constructors)

就当作是在结构体和类中方便输入数据的函数吧()

一般建议使用初始化列表(Initializer list)

ClassName(parameters) : member1(initialValue1), member2(initialValue2), ... {
// 构造函数体(附加逻辑)
}

C++ Data Structures

STL is a library that consist of different data structures and algorithms to effectively store and manipulate data.合理地选择可以带来更高的效率.

STL由容器,迭代器(iterators)和算法组成,算法通过迭代器访问和操作数据结构.

Vector

some useful functions:front()/end(),begin()/end(),push_back()/pop_back()

at():在指定索引中访问元素,和下标访问等效,好处是越界访问会返回错误信息.

size(): 返回元素数量

empty(): 检查是否为空(返回bool值)

Loop through vector

for (auto x:v){cout<<x<<"\n";} //c++11及以上
  • auto 的作用是让编译器自动推导 x 的类型,使代码更简洁灵活.
  • 以上循环为拷贝操作,若要修改容器元素,需要使用引用&. 如果还想避免意外改动,还可以再加上const
for (int i = 0; i < v.size(); i++) {}

List

和vector的主要区别在于支持开头添加元素,不支持随机访问(索引访问)

Add elements

special:在头部添加使用push_front()

只支持范围for循环和迭代器

Stacks

可以想象堆叠着的煎饼,只能处理顶部的元素(煎饼)bypop()&push(),即”LIFO(last in, first out)”

Queue

和堆栈相反,FIFO(first in, first out)

C++ Iterators

用来遍历容器

for (it=name.begin(),it!=name.end();++it){}

冒泡优化

//improve
bool swapped;
for (int i=0;i<n;i++){
swapped=false;
for (int j=0;j<n;j++){
if(s[j]>s[j+1]) swap(s[j],s[j+1]);
swapped=true;
}
if (!swapped) break;
}

使用 swapped 标志位,检测是否发生了交换。如果某次内循环没有交换,说明数组已经有序,可以提前退出外层循环,避免不必要的操作. 可以看出冒泡的教学意义,但真到要排序的时候,还是用sort函数.

string to int

其他数据类型转化为string类型时可以使用to_string()函数,但字符型转化为整型呢? 利用ASCII 编码 的性质,进行ch - '0'运算时,字符会隐式地被转换为它们对应的 ASCII 值,并进行整数运算。这在处理字符串和数字的混合数据时非常有用.

e.g.将string类型字符串转换为整型十进制数.

//将一个字符串转化为十进制整数
#include <bits/stdc++.h>
using namespace std;
int main() {
string ch_number="114514";
int number = 0;
for (char ch : ch_number) {//在循环中实现累乘
number = number * 10 + (ch - '0');
}
cout<<number;
return 0;
}

好啦,现在你获得了一个可以用于数学计算的数字!

Math

  • (较)精确的pi
#include <cmath>
const double pi=acos(-1.0);
  • 十的六次方表示为1e6

格式化输出

保留指定位小数

#include <iomanip>// std::fixed, std::setprecision
cout<<fixed<<setprecision(n);//保留n位小数.
  • std::fixed:设置浮点数的表示方式为定点表示(即小数形式,而非科学计数法).如果没有 std::fixedstd::setprecision 会影响数字的总有效位数.

  • 流操控符的设置会一直影响之后的输出,直到被重新设置或流被刷新。

    //使用完后重置格式
    cout.unsetf(ios::fixed);

因为setprecision(设置精度)实在是有点难记,还是用printf罢

5.

6.阶乘的各种实现方法

  • 递归:n!=n×(n−1)!

    long long factor(int n){
    if (n==0||n==1) return 1;
    return n*factor(n-1);
    }
    //使用尾递归优化
    long long factorialTail(int n, long long result = 1) {
    if (n == 0 || n == 1)
    return result;
    return factorialTail(n - 1, n * result); // 尾递归调用
    }

7.获取两个变量中的较小(大)值

  • 三元表达式

    for (int i=1;i<=(s1<s2?s1:s2);i++)
  • min(),max()

    for (int i=1;i<=min(s1,s2);i++)

8.快捷头文件

#include <bits/stdc++.h>

包含所有常用的 C++ 标准库头文件,从而不用单独导入每个需要的头文件。

bits/stdc++.h 是 GCC 专用,并不是 C++ 标准的一部分,因此可能无法在非 GCC 编译器(如 MSVC、Clang)上运行。

9.shortening code

//shortening code by Macros
#define ll long long
//or by typedef
typedef long long ll

11.获取保留空格的字符串

getline(cin,name);

vector容器预先分配空间

由vector容器的动态增加原理,在处理大量输入时会导致庞大的内存开销.

解决:预先分配空间(Pre-allocating space)

cin>>n;
vector<int> numbers(n);
for (int i=0;i<n;i++) cin>>numbers[i];

Optimize Input/Output for faster I/O

ios::sync_with_stdio(false);
cin.tie(nullptr);// Disable synchronization

FAQ

溢出和精度

棋盘中的矩阵

Fact:对于一个n*m规格的棋盘,其包含的矩阵数量为

unsigned long long rectNum(int m,int n){
return (1ULL*(n+1)*(m+1)*m*n)/4-sqrNum(m,n);
}

Why?

For rectangles, any subgrid defined by two distinct horizontal lines and two distinct vertical lines forms a rectangle.

所以,原问题便转化为组合问题.

1.溢出问题

这里的乘法 (n+1)*(m+1)*m*n 可能会在乘法过程中溢出,因为所有乘法在计算时仍然会使用普通整型规则进行中间计算。因此,即使结果存储在 unsigned long long 中,可能已经溢出了。

solve:使用 1ULL 强制转换为 unsigned long long

In C++, when you multiply two integers, the multiplication happens with the type of the operands. If both operands are int (typically 32 bits), the result will also be treated as an int, even if it’s stored in a larger type later.

Here, 1ULL forces the multiplication to be done in unsigned long long space, avoiding overflow.

It’s a common practice when dealing with large numbers or potential overflow in C++ arithmetic.

(Generated by ChatGPT 4o)

2.在(n+1)*(m+1)*m*n中肯定存在两个偶数项,确保了结果总能被 4 整除,因此,整数除法的精度问题在这里不存在

结构体还是函数?

结构体(或类)主要用于将多个相关变量组合在一起,表示一个逻辑实体。非常适合以下场景:

  • 多个变量需要频繁一起使用,且这些变量之间有逻辑关系(如 point 包含 xy)。
  • 操作多样化:结构体可以包含成员函数,对数据进行封装和操作。
  • 数据共享与存储:多个实例表示不同的数据组时,如存储多个矩形板的信息。

函数更适合以下情况:

  • 参与计算的变量数量较少,且这些变量没有内在的关联关系(如纯数学计算)。
  • 一次性操作或逻辑简单:函数只做某一特定任务,不需要保存状态。
  • 独立计算逻辑:函数可以被独立调用,不依赖结构体的成员变量。

总之, 变量数量和关联性是决定使用结构体还是函数的重要因素。结构体适合将相关数据和操作封装在一起,函数适合独立、简单的计算逻辑。

std::sort的比较函数

通过比较出生日期对通讯录进行排序

struct date{
int y,m,d;
};
struct pb{
char name;
date bir;
string num;
};
bool prepare(const pb& a, const pb& b){//const防止意外修改,&避免拷贝
if (a.bir.y!=b.bir.y){
return a.bir.y<b.bir.y;
}
if (a.bir.m!=b.bir.m){
return a.bir.m<b.bir.m;
}
return a.bir.d<b.bir.d;
}

提问:为什么不是返回a.bir<b.bir?这可以吗?

std::sort 需要一个具体的比较函数来比较两个 pb 结构体的内容。要

简化逻辑,可以在结构体pb中重载 operator<

//修改pb
struct date {
int y, m, d;

// 重载 operator< 用于比较日期
bool operator<(const date& other) const {
if (y != other.y) {
return y < other.y;
}
if (m != other.m) {
return m < other.m;
}
return d < other.d;
}
};

重载是什么?