算法基础 - c++语法

本文详细探讨了选择C++的原因,包括其高性能、低级别硬件访问、编译时优化、资源管理优势以及C++标准库的效率。文章还涵盖了输入/输出优化、判断语句、循环结构、STL容器、位运算符、时间复杂度分析和常见数据结构的操作方法,以及编程实践中的注意事项。
摘要由CSDN通过智能技术生成

选择 C++ 的原因:性能高

  1. 低级访问和控制: C 和 C++ 提供了对硬件的直接访问和控制能力。例如,你可以直接管理内存、使用指针操作等,这在许多高级语言中是不可能或受限的。
  2. 编译时优化: C 和 C++ 编译器能够在编译时进行广泛的优化,如内联函数、循环展开、常量传播等,这些优化有助于提高运行时性能。
  3. 无运行时开销:与需要运行时环境(如Java的JVM或Python解释器)的语言相比, C 和 C++ 编译的程序直接转换为机器码,无需额外的运行时支持,这减少了执行时的开销。
  4. 确定性的资源管理: C 和 C++ 允许程序员精确控制资源的使用,如手动管理内存分配和释放。这种控制能力使得资源利用更加高效,尤其在资源受限的环境下。
  5. 标准库的效率:C 和 C++ 的标准库(如STL)高效且经过优化,提供了快速的数据结构和算法实现。
  6. 硬件依赖优化:C 和 C++ 代码可以针对特定硬件平台进行优化,利用特定的指令集和硬件特性来提高性能。

输入、输出和语句控制

#include <cstdio>
printf / scanf 所需头文件

#include <iostream>
cin / cout 所需头文件

// 优化 cin / cout 速度
std::ios::sync_with_stdio(false);
std::cin.tie(0);  // 如果编译开启了 C++11 或更高版本,建议使用 std::cin.tie(nullptr);

输入优化 - 快读

getchar 是用来读入 1 byte 的数据并将其转换为 char 类型的函数,且速度很快

int read() {
  int x = 0, w = 1;
  char ch = 0;
  while (ch < '0' || ch > '9') {  // ch 不是数字时
    if (ch == '-') w = -1;        // 判断是否为负
    ch = getchar();               // 继续读入
  }
  while (ch >= '0' && ch <= '9') {  // ch 是数字时
    x = x * 10 + (ch - '0');  // 将新读入的数字「加」在 x 的后面
    // x 是 int 类型,char 类型的 ch 和 '0' 会被自动转为其对应的
    // ASCII 码,相当于将 ch 转化为对应数字
    // 此处也可以使用 (x<<3)+(x<<1) 的写法来代替 x*10
    ch = getchar();  // 继续读入
  }
  return x * w;  // 数字 * 正负号 = 实际数值
}

输出优化

putchar() 用来将数字输出为单个字符的函数

void write(int x) {
  static int sta[35];
  int top = 0;
  do {
    sta[top++] = x % 10, x /= 10;
  } while (x);
  while (top) putchar(sta[--top] + 48);  // 48 是 '0'
}

变量类型

变量类型所需字节
booltrue / false1 byte
char‘a’ 、‘b’1 byte
int-2147483648 ~ 21474836474 byte
float1.23 2.5 6-7 位有效数字4 byte
double15 - 17 位有效数字8 byte
long long-2 ^ 63 ~ 2 ^ 63 - 18 byte

格式输出符号

输出类型格式输出
int%d
double%lf
long double%llf
float%f
char%c
string%s

判断语句

if (flag)
{
    statement;
}
else if (flag)
{
    statement;
}
else
{
    statement;
}

循环结构

while (flag)
{
    statement;
}

当 flage 满足条件时,执行 statement 语句。

do
{
    statement;
}
while (flag);

先执行一次 statement 语句,再进行判断。

for(init-statement; confident; expression)
{
    statement;
}
  • init-statement 可以为空
  • confident 可以为空,默认为 true
  • expression 可以为空

跳转语句

  • break 退出循环
  • continue 进入下一轮循环

STL 容器与算法

vector 变长数组
string 字符串 substr(), c_str()
queue 队列 push() front() pop()
priority_queue 优先队列 push() top() pop()
stack 栈 push() top() pop()
deque 双端队列 
set map multiset multimap 给予平衡二叉树(红黑树),动态维护有序序列
unordered_set unordered_map unordered_multiset unordered_multimap 哈希表
bitset 压位

系统为某一程序分配空间时,所需时间与空间大小无关,与申请次数有关。

容器共同函数

  • size() :返回容器里元素的数量
  • empty() :返回容器是否为空
  • clear() :清空所有元素(队列、优先队列、栈不支持)

pair

pair<int, int> p;

// 创建一个 pair
p = {a, b};
p = make_pair(a, b);

p.first 第一个元素
p.second 第二个元素

vector

基于数组实现,动态数组:当阈值超过一定数量时,会进行扩容。
访问: O ( 1 ) O(1) O(1)
插入 / 输出: O ( n ) O(n) O(n)

  • front() :返回第一个元素
  • back() :返回最后一个元素
  • push_back() :在容器最后添加一个元素
  • pop_back() :删除容器最后一个元素

string

查找 O ( n ∗ k ) O(n * k) O(nk)

  • find() :查找子字符串第一次出现的位置。
  • substr() :返回指定位置、指定长度的子字符串。 O ( k ) O(k) O(k)
  • c_str() :返回一个可以 %d 输出的 char * 字符串。

queue

先进先出
增删改查的时间复杂度: O ( 1 ) O(1) O(1)

  • push() 向队尾插入一个元素
  • front() 返回队头第一个元素
  • back() 返回队尾最后一个元素
  • pop() 弹出队头第一个元素

deque

以下操作时间复杂度: O ( 1 ) O(1) O(1)

  • front() 访问队前第一个元素
  • back() 访问队尾最后一个元素
  • push_back() 向队尾插入一个元素
  • pop_back() 弹出队尾一个元素
  • push_front() 向队头插入一个元素
  • pop_front() 向队头弹出一个元素

priority_queue 默认大根堆

访问: O ( 1 ) O(1) O(1)
插入 / 删除 : O ( n ) O(n) O(n)

  • push() : 向堆中放入一个元素
  • top() :获取堆顶第一个元素
  • pop() :弹出堆顶第一个元素
priority_queue(int, vector<int>, greater<int>> heap; 小根堆
    第一个参数:堆中元素的类型
    第二个参数:堆由什么结构实现。默认 vector
    第三个参数:如何排序的

stack

先进后出
时间复杂度: O ( 1 ) O(1) O(1)

  • push() :向栈顶添加一个元素
  • top() :访问栈顶第一个元素
  • pop() :弹出栈顶第一个元素

set / multiset

set 不能存在重复元素 / multiset 可以存在重复元素

  • insert()
  • find()
  • count()
  • erase()
    • 输入一个数:删除所有这个数
    • 输入迭代器:删除迭代器
  • lower_bound() 返回大于等于 x 最小的数的迭代器
  • upper_bound() 返回大于 x 最小的数的迭代器

map / multimap

可以向使用数组一样,使用 map

  • insert() 插入一个 pair
  • erase()
  • find()

bitset

  • ~ & | ^

  • <<

  • == !=

  • []

  • count)(

  • any() 判断是否有一个 1

  • none() 判断是否都为0

  • set()

  • reset

  • flip() 取反

迭代器

通过统一的接口,对不同的容器进行遍历,类似与指针,可以进行解引用。

  • begin() :指向容器中第一个元素
  • end() :指向容器中最后一个的后一个位置(属于越界范围)

注:绝大多数容器均可以通过迭代器遍历容器里的元素,除了队列、栈、优先队列(因为他们本身用于不同的规则,即不需要遍历元素,以破坏这个规则)。

位运算符

  • & :与
  • | :或
  • ^ :异或
  • ~ :取反

C++ 库

  1. std::sort(first, last) 对 [first, last) 容器进行排序。左闭右开。first / last 为迭代器。
// 默认从小到大排序
vector<int> nums = {2, 4, 3, 5};
sort(nums.begin(), nums.end());

int a[20];
sort(a, a+20);

// 从大到小
sort(s.begin(), s.end(), greater<int>());
  1. std::unique() 删除连续的重复项
// 去重(先排序,在 unique )
vector<int> nums = {1, 2, 1, 2, 3, 4, 5, 5};
sort(nums.begin(), nums.end());
auto last = unique(nums.begin(), nums.end());
nums.erase(last, nums.end());
  1. random_shuffle() 打乱
  2. lower_bound / upper_bound 二分
lower_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp) 返回大于等于 x 最小的数的迭代器

upper_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp) 返回大于 x 最小的数的迭代器

// 第四个参数可选
  1. next_permutation() 排序
next_permutation(a, a+n)
  1. swap() 交换两个数

C 库

  • strlen() : 返回给定字符串的长度。
int strlen(const char* str);
  • strcpy() : 复制字符串。
char* strcpy(char* dest, const char* src );
  • strcmp() : 按字典比较两个字符串。
int strcmp(const char* lhs, const char* rhs );
  • strchr() : 查找字符。
const char* strchr(const char* str, int ch );
  • strstr() :
const char* strstr( const char* haystack, const char* needle );
  • strcat() : 拼接字符串。
char* strcat( char* dest, const char* src );
  • memset() : 设置一块内存的默认值
void* memset( void* dest, int ch, std::size_t count );

数据生成

  • srand(time(NULL))

调试代码模板

#define DEBUG
#ifdef DEBUG
// do something when DEBUG is defined
#endif
// or
#ifndef DEBUG
// do something when DEBUG isn't defined
#endif

时间复杂度

一般 ACM / CSP 笔试题的时间限制是1秒或2秒。
c++ 一般 1s 以内可以算 1 0 7 ∼ 1 0 8 10 ^ {7} \sim 10 ^ 8 107108

数据量时间复杂度算法
n ≤ 30 n \le 30 n30 O ( n 3 ) O(n^{3}) O(n3)dfs+剪枝 / 状态压缩 dp
n ≤ 100 n \le 100 n100 O ( n 3 ) O(n^{3}) O(n3)floyd / dp
n ≤ 1000 n \le 1000 n1000 O ( n 2 ) 或 O ( n 2 log ⁡ n ) O(n^{2})或O(n^{2} \log{n}) O(n2)O(n2logn)dp / 二分
n ≤ 10000 n \le 10000 n10000 O ( n n ) O(n \sqrt{n}) O(nn )块状链表
n ≤ 100000 n \le 100000 n100000 O ( n log ⁡ n ) O(n \log{n}) O(nlogn)各种 sort ,线段树,树状树状、set/map、heap、dijkstra + heap、spfa 、求凸包、求半平面交、二分
n ≤ 1000000 n \le 1000000 n1000000 O ( n ) O(n) O(n)以及常数较小的 O ( n log ⁡ n ) O(n \log{n}) O(nlogn) 算法 => hash 、 算指针扫描 、kmp、AC 自动机 、常数比较小的 O ( n log ⁡ n ) O(n \log{n}) O(nlogn) 的做法:sort,树状数组、heap、dijkstra\spfs
n ≤ 10000000 n \le 10000000 n10000000 O ( n ) O(n) O(n)双指针扫描,kmp,AC 自动机 \ 线性筛素数
n ≤ 1 0 9 n \le 10^{9} n109 O ( n ) O(\sqrt{n}) O(n )判断素数
n ≤ 1 0 18 n \le 10^{18} n1018 O ( log ⁡ n ) O(\log{n}) O(logn)最大公约数

注意事项

  1. printf / scanf效率一般比 cin / cout
  2. 万能头文件 #include <bits/stdc++.h (通用头文件,缺点编译慢)。
  3. 使用 scanf("%c", &n)时,会读取 \n等空白符。
  4. 当使用 cout 输出一个浮点数时,如果这个数的大小超过了 cout 的默认精度(通常为 6 位数字),它可能会自动转换为科学计数法来表示。这是为了避免输出过长的数字,同时保持一定的精度。
  5. 由于浮点数在计算机中的存储方式导致的,它们是以二进制形式表示的,而某些十进制小数在二进制中是无限循环的,不能精确表示。
  6. 使用 std::getline 函数,从输入流中读取一行。它会读取直到遇到换行符 \n,但不会将换行符添加到结果字符串中。换行符会被从缓冲区丢弃。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初原挽风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值