这些c++的基础知识,你都知道吗
1. 清空数组:
memset(a, n, m): 包含在<cstring>头文件
将数组a第n个字节到第m个字节清空。
sizeof a 可以算出每个数组占用的字节数量
2.复制数组
memcpy(b, a, m): 包含在<cstring>头文件
将数组a复制给b数组,m表示复制多长
sizeof a 可以算出每个数组占用的字节数量
3.内置最大最小值:
INT_MAX和INT_MIN, 包含在头文件<limits.h>中
4.倒置函数:
reverse,头文件是algorithm需要填入首尾地址进行倒置数组或字符串
reverse(&a[0], &a[n])5.字符数组要比字符长度多一,因为要存一个 \0 表示结束
6.读入一行字符:
fgets(s, n, stdin) 存入字符数组s(char[]),读n个,从stdin文件读入,系统的一个变量
getline(cin, s) 存入字符串s(string),读取一行
cin.getline(s, 100) 存入字符数组s(char[]), 读100个
7.字符数组函数
cstring头文件中
strlen(s) //返回字符数组s的长度,不包含回车
strcmp(a, b) // 按照字典序比较,大于 1 ;等于 0; 小于 -1;
strcpy(a, b) //将字符数组b复制给字符数组a
8.自己实现求长度函数:
int len = 0;
for (int i = 0; s[i]; i ++) len ++ ;
9.字符串 string s
用printf输出string字符串:printf("%s\n", s.c_str());
用s.empty()判断字符串是否为空
用s.size()返回字符串的长度
用s.substr(n, m)返回从n开始长度为m的字符,返回子字符串,只填写一位数字n,就默认返回[n,s.size())
用s.back()可以返回字符串最后一个字符
用s.pop_back()可以去掉字符串最后一个字符
可以用 运算符号直接进行运算(只能用+)和比较
10.c++特有的遍历string字符串方式:
for (char c : s) cout << c << endl;
char 表示字符串s中每一个元素的数据类型,c单独表示字符串s中的一个元素,s表示需要遍历的字符串,
如果需要改变s里的值则在c前面加一个取地址符号&即可
11将字符串变成字符串流,即将字符串以空格分开:
#include <sstream>
.stringstream ssin(s); //将字符串s变成字符串流
string str;
ssin >> str; //将字符串流读入
13.swap函数:
交换两个变量, 头文件为<algorithm>
14.静态变量只执行一次 :
大致上等价于开了一个只能在函数内调用的全局变量
static int n = 0;
15.c++引用:&
函数传参的时候,加上引用,可以改变外部传入函数的参数的实际值
数组传递默认为引用传递
16.inline
可以加在函数定义前,表示将该函数内的代码直接替换到主函数中
17.类和结构体:
唯一区别,struct中默认为共有变量,类中默认为私有变量
class Person
{
private://后面的东西是私有的,只能在该类中调用public: //后面的东西是公共,外部可以调用
}; //一定要加上分号
同一个类中可以出现多个private和public,他们后面的变量和函数统一称为成员变量
18.类和结构体的构造函数:
struct Person
{
int age, height;
double money;Person() {} //第一种构造函数类型 空
Person(int _age, int _height, double _money) //第二种构造函数
{
age = _age;
height = _height;
money = _money;
}
Person(int _age, int _height, double _money) : age(_age), height(_height), money(_money) {}第二种构造函数的快捷写法
};int main()
{
Person p1; //调用第一种,没给初始值,调用默认构造函数
Person p2(18, 180, 100); //调用第二种构造函数 进行赋值
Person p3 = {18, 180, 100); //根据定义顺序进行赋值, 在没有对应构造函数时,会报错return 0;
}
19.变量用 . 访问, 指针用 -> 访问
20.将字符串转换为整数:
stoi函数 stoi("123"),直接将字符串123变成数字123 ,参数是string类型
atoi函数 ,参数是char类型
20.容器
vector:变长原理是每次不够用就将数组扩大到自身的两倍【倍增】
头文件:<vector>
是一个变长数组,支持随机访问,不支持任意位置O(1)插入
vector<type> a; type可以是各种基本类型以及结构体
a.size() 得知长度 时间复杂度为O1
a.empty() 得知是否为空 时间复杂度为O1
以上两个函数基本上所有容器都有
a.clear() 清空所有元素 时间复杂度为O1
迭代器:相当于地址
vector<int>::iterator it; 可以把vector的迭代器与一个整数相加减,与指针的移动类似, 可以用*it取得迭代器的值
每一个vector都会有两个特殊的迭代器:[begin,end)左闭右开
a.begin() a的第一个元素的地址
a.end() a的最后一个元素的位置的下一个位置的地址
遍历方式:
vector<int> a({1, 2, 3});
一、数组遍历的方式
for (int i = 0; i < a.size(); i ++ ) cout << a[i] << ' ';
二、迭代器遍历
for (auto i = a.begin(); i != a.end(); i ++) cout << *i << ' ';
三、范围遍历
for (int x : a) cout << x << ' ';
a.front() 返回第一个元素,等价于a[0]和*a.begin();
a.back() 返回最后一个元素,等价于a[a.size() - 1]a.push_back(x) 在最后一个元素之后加一个元素x,时间复杂度为O1
a.pop_back() 删除最后一个元素,时间复杂度为O1
很重要的一个数据结构
队列:queue 以下三个容器没有clear()函数,其他都有, 若需要清空一个队列,则进行重新初始化即可:q = queue<int>();
头文件:queue, 包含了队列queue和优先队列priority_queue两个容器(默认是大根堆)队列先进先出,相当于一个管道左进右出
queue<int> q;
普通队列都是循环队列: queue
q.push(1) 队头插入一个元素
q.pop() 队尾弹出元素
q.front() 返回队头元素
q.back() 返回队尾元素优先队列:每次弹出所有数的最大值,默认是大根堆
priority_queue<int> a;
a.push(1) 插入一个元素,插入的位置不确定
a.top() 取出最大值
a.pop() 删除最大值
若是自己定义结构体类型的优先队列,需要重载小于号
struct Rec
{
int a, b;
bool operator< (const Rec& t) const
{
return a < t.a;
}
};priority_queue<Rec> d;
d.push({1, 2});
优先队列:小根堆,每次弹出所有数的最小值
priority_queue<int, vector<int>, greater<int>> b;
若是自己定义结构体类型的优先队列,需要重载大于号
struct Rec
{
int a, b;
bool operator > (const Rec& t) const
{
return a > t.a;
}
};priority_queue<Rec, vector<Rec>, greater<Rec>> d;
d.push({1, 2});
栈:
用法和队列类似,不过是先进后出,队尾插入队尾弹出,一端
头文件<stack>
定义:stack<int> stk;
stk.push(1) 向栈顶插入一个元素
stk.pop() 弹出栈顶元素
stk.top()
双端队列:随机存储,相当于扩展版的vector(队尾插入O1),队头队尾插入都是O1
头文件<deque>
定义:duque<int> a;
a.begin(), a.end()
a.front(), a.back();a.push_back(1), a.push_front(2);
a[0];随机访问一个元素
a.pop_back(), a.pop_front()a.clear();
set:
动态维护一个有序的集合
头文件:<set>
有两种容器:
set <int> a; 元素不能重复,插入重复的元素会被忽略
multiset<int> b; 元素可以重复
内部实现和函数基本相同,也可以定义结构体类型的set,但需要重载小于号
size/empty/clear,迭代器与vector类似a.insert(x) 插入一个元素
a.find(x) 查找一个元素,会返回x的迭代器
if (a.find(x) == a.end()) //判断x在a中是否存在a.lower_bound(x) 找到大于等于x的最小的元素的迭代器
a.upper_bound(x) 找到大于x的最小的元素的迭代器a.erase(it) 删除it的迭代器
a.count(x) 返回x的个数,没有就会返回0迭代器可以理解成指针
map: 【特别有用】 pair类似【自己认为】
头文件:<map>,原理与set一样,但用法神奇
定义:
map<type1, type2> a;
可以看成数组,相当于type1是下标,type2是数组下标对应的值
size/empty/clear/begin/end/insert/earse/find 的用法与set类似
a.insert({type1, type2});
unordered_set:做不了二分,目前一些比赛不支持该容器
无序的集合,与set相对,用法与set完全一致,除了没有a.upper_bound(x)a.lower_bound(x)两个函数
unordered_set<int> a;
unordered_multiset<int> b;
21.位运算:
与 & and
或 | or
非 ~ not
异或 ^ xor
对于一个整型进行位运算,计算机是将该数转换为二进制数,一位对一位得出结果,输出该二进制数的十进制数
将整数转换为二进制进行移动,再转换为十进制数输出
左移 << ,在最后一位后补0 等价于* pow(2,k)
右移 >>将最后一位移出去,即抹去,右移等价于 / pow(2,k)
常用操作:
(1)求x的第k位数字: x >> k & 1
(2)lowbit(x) = x & (~x + 1), 返回x的最后一位1以及后面的数字
22.常用库函数:都在algorithm库中,算法库
(1)reverse 翻转 On
翻转一个vector:
reverse(a.begin(), a.end());
翻转一个数组:
reverse(a, a + size)传入首地址和尾地址后一个地址
(2)unique 去重,删去重复元素,需要保证相同元素挨在一起,原理是将去重后的数据放在前面,其他放在后面
会返回去重后的末尾元素的下一个地址
int a[] = {1, 1, 2, 2, 3, 4, 5};
int m = unique(a, a + 7) - a; //去重的同时,返回尾地址元素的下一个地址,减去首地址元素,等于之间的数据量
cout << m << endl;
如果是vector可以这样:
vector<int> a({1, 1, 2, 2, 3, 4, 5});
a.erase(unique(a.begin(), a.end()), a.end());
这句话完成了两个操作,一是去重,二是通过返回的新的地址,将新地址到末尾的元素全部删掉
for (auto x : a) cout << x << ' ';
(3)random_shuffle 打乱顺序
random_shuffle(a.begin(), a.end()); 但每次打乱的顺序都一样
想要达到不一样的效果:
先在头文件加上<ctime>加上时间
然后加上随机种子 srand(time(0)); //这个时间函数返回1970年到当前的时间秒数
(4)sort函数 【【【【超级重要】】】
sort(a.begin(), a.end()); 从小到大排
sort(a.begin(), a.end(), greater<int>()); 从大到小排
若想要按照自己想的方式排序,可以自己定义一个cmp函数,然后
bool cmp (int a, int b) //这里可以理解为 a 是否应该排在 b的前面
{
return a < b;
}sort(a.begin(), a.end(), cmp); 即可
也可以排结构体:
bool cmp (Rec a, Rec b)
{
return a.x < b.x
}或者在结构体的定义中重载小于号
(5)lower_bound/upper_bound 二分
传入的参数需要已经排好序了
lower_bound(a, a + size, x) 在a中查找x,返回第一个大于等于x的元素的迭代器, 传入首地址,尾地址后一位
upper_bound(a, a + size, x) 在a中查找x,返回第一个大于x的元素的迭代器
(6)next_permuatatin(a.begin(), a.end()),
从小到大,进行返回下一次全排列
若后面不能排序了,就会返回false,反之返回1