以下内容来源于柳神《PAT | 蓝桥 | LeetCode学习路径 & 刷题经验》文章中的部分内容,仅供自己学习使用。若有能帮助到大家的请移步到柳神的博客查阅原版。侵删!
dev中查看 vector
的值:*(&v[0])@3
——必须对 v[0]
变换,先取地址再取值, @
后⾯的数字 3
能让dev显示数组前3个的值,在⽤这个之前要先在编译选项-编译器⾥⾯ -std=C++11
INT_MAX
、INT_MIN
、LLONG_MAX
在 #include <climits>
头⽂件⾥⾯, int最⼤值为2147483647,共10位数字;LLONG_MAX
最⼤值有19位数字,以9开头。所以说储存13位的学号可以⽤long long int
,输出的时候使⽤%013lld
对char c[15]
进行 sort
排序的 cmp(char a[], char b[])
函数要这样写:strcmp(a, b) < 0
,因为 strcmp
返回的不是-1 0 1
⽽是等于0 ⼩小于0 ⼤大于0
的某个值,strcmp
在头⽂文件 #include <string.h>
⾥⾯
对 vector v
或者string v
进行 sort
排序: sort(v.begin(), v.end(), cmp1);
;对数组a进⾏sort排序:sort(a, a + n, cmp1);
将十进制a
转换为b
进制数,当 a
不等于 0
时,将a%b
从后往前倒序保存下来,每次保存后将 a/b
。这样倒序保存的数就是⼗进制a
在b
进制下的结果。
// 判断n是否为素数
bool isprime(int n) {
if (n <= 1) return false;
int sqr = int(sqrt(n * 1.0));
for (int i = 2; i <= sqr; i++)
if (n % i == 0) return false;
return true;
}
比较有学号、姓名、分数的学⽣成绩排名,⽤结构体数组存储,用sort
函数对 node a, node b
进行sort
排序, cmp
函数根据情况写:cmp
函数中:升序:a < b
;降序:a > b
;不降序: a <= b
所谓two pointers问题,就是用 p
、q
两个指针,使用 while
语句处理链表问题,链表什么的
⽤vector
存储就好了~
输出语句如果有 is / are、加s / 不加s的区别的话一定要当心,比如 "There is 1 account"
和 "There are 2 accounts"
是不同的输出语句
移除字符串s
的第一个字符: s.earse(s.begin());
在字符串s
前面增加 n
个 0
:s.insert(0, n, '0');
给一些数字字符串,求这些字符串拼接起来能够构成的最小的数字的方式:用cmp
函数就能解
决: bool cmp(string a, string b) {return a + b < b + a;}
学生姓名和分数,如果不需要将姓名输出的话,不仅可以用 struct
结构体中string
或者 char
存储姓名,还可以将姓名转化为int
类型,比如 ABC4
可以转换为((((A * 26) + B) * 26) + C) * 10 + 4 = ((((1 * 26) +2) * 26) + 3) * 10 + 4
用数组 hash[26]
或者 hash[10]
保存某个字母或者数字出现的次数/是否曾经出现过;用 hash[256]
保存某个ASCII
码 字符是否出现过, exist[10000]
、 cnt[10000]
同理
题目输入中所给的a b区间可能a和b给的顺序反了,要比较一下a和b的大小,如果a>b
记得swap
回来
~
char a[]
的⻓长度要用 strlen(a)
; 得到的长度是里面真实存储了字母的长度,不包括\0
要输入一行的数据的话:
如果是 string s
,则用 getline(cin, s);
在头文件#include <string>
里面;
如果是 char str[100]
, 则用 cin.getline(str, 100);
在头文件#include <iostream>
里面,也可以用 gets(str);
遇到链表中有无效结点还要排序的,在结构体里面加一个bool flag = false;
变量,将有效结点的 flag
标记为 true
,并统计有效节点的个数cnt
,然后在 cmp 函数中这样写:
bool cmp(node a, node b) {
if(a.flag == false || b.flag == false)
return a.flag > b.flag;
else
return a.value < b.value;
}
这样flag == false
的会自动变换到最后面去,到时候输出前cnt
个有效的结点就可~
如果问年龄区间内的前100个,而数据量很庞大的话,在处理数据的时候就可以把每个年龄的前100个
选出来,然后再遍历,因为无论如何也不会超过100个,最极端的情况就是当前区间只包含一个年龄~
// 素数表的建⽴立
// ⾸首先都标记为1,外层循环i从2到√n,内层循环j从2开始到i*j<n 把j的i倍都标记为0
vector<int> isprime(50000, 1);
for (int i = 2; i * i < 50000; i++)
for (int j = 2; j * i < 50000; j++)
isprime[j * i] = 0;
集合s或者映射s中寻找是否存在某一数字: s.find(num) != s.end()
在迭代遍历集合s或者映射s时,auto it = s.begin(); it != s.end(); it++
set
获取元素值:*it
map
获取元素值:it->first it->second
// 关于pair的⽤用法
// pair是⼀一个变量量类型,两两⼀一对组成⼀一个变量量,可以⾃自定义pair中两个元素的类型,⽐比如string和int, string和double, double和string等
// pair<string, int>就是string和int类型的⼀对pair类型,为了了简便便书写,我们⼀一般会将pair<string, int>使⽤用typedef重命名为p之后再使⽤用,避免每次⽤用的时候都写很⻓长的pair<string,int>
#include <iostream>
#include <vector>
using namespace std;
int main() {
typedef pair<string, int> p; // 建⽴立string和int类型的⼀一对pair类型,并将类型命名为p
vector<p> v; // 建⽴立p类型的数组vector v
p temp = make_pair("abc", 123);// "abc"和123组成的pair赋值给pair类型的临时变量量temp
v.push_back(temp);// 将临时变量量temp放⼊入v数组中
cout << temp.first << " " << temp.second << endl;// 输出temp的第1个元素和第2个元素
cout << v[0].first << " " << v[0].second << endl;// 输出v[0]的第1个元素和第2个元素
return 0;
}
当心两个数累加或者累乘过程中的超出定义的类型范围,如果是分子分母都是long long int
类型的分数, (分子+分子) / 分母的累加,记得在累加过程中约分,以保证不超出long long int
范围~
abs()
在头⽂文件#include <stdlib.h>
⾥里里⾯面
% 1000000007
如果怕溢出可以多取余几次,比如 result = (result + (countp * countt) % 1000000007) %1000000007;
// 树的遍历-前序中序转后序:
void post(int root, int start, int end) {
if (start > end) return;
int i = start;
while (i < end && pre[root] != in[i]) i++;
post(root + 1, start, i - 1);
post(root + 1 + i - start, i + 1, end);
printf("%d ", pre[root]);
}
// 树的遍历-后序中序转前序:
void pre(int root, int start, int end) {
if (start > end) return;
int i = start;
while (i < end && post[root] != in[i]) i++;
printf("%d ", post[root]);
pre(root - 1 - end + i, start, i - 1);
pre(root - 1, i + 1, end);
}
树的遍历-后序中序转层序,只需在转前序的时候,加一个变量index,表示当前的根结点在二叉树中
所对应的下标,根结点为0
,左子树根结点为 2 * index
,右子树根结点为2 * index + 1
,将转的过程中产生的post[root]
的值存储在level[index]
中, level
数组一开始都为-1
,这样将所有不是 -1
的点输出即为层序的输出~
树的遍历-后序中序转之字形,用level
上面那个方法就比较不方便,可以在转前序的时候,加一个变
量level
,一开始为0
,每一次 level+1
,建立一个 vector<int> v[10000]
,则每次将得到的前序的值
放入v[level].push_back()
,得到的就是一个每一行代表树的一层的二维数组,然后对每一行之字形输
出~
建立树的方式也可以用邻接表,建立结构体 struct TREE
,包含left
和right
两个变量,然后以建立TREE
的二维数组 tree
: vector<TREE> tree[n];
,然后将tree[i]
的 left
和right
赋值为i
的结点的左右子树的下标:tree[i].left= ;tree[i].right = ;
// ⼆二叉搜索树的递归建树⽅方法:
node* build(node *root, int v) {
if(root == NULL) {
root = new node();
root->v = v;
root->left = root->right = NULL;
} else if(v <= root->v)
root->left = build(root->left, v);
else
root->right = build(root->right, v);
return root;
}
for(int i = 0; i < n; i++) {
scanf("%d", &t);
root = build(root, t);
}
求最短路径过程中,由dfs求得的路径path是逆的,需要倒置输出~string
类型转int
、 long
、 long long
、 float
、 double
、 long double
// 头文件#include <string>
:stoi
、stol
、 stoll
、stof
、stod
、 stold
char c[10]
类型转 int
、long
、long long
// 头文件#include <cstdlib>
: atoi
、 atol
、atoll
int
、long
、 long long
、 float
、 double
、 long double
转 string
// 头文件#include <string>
: to_string
使用reverse
倒转vector v
或者数组v
里面的值 // 头文件#include <algorithm>
,使用⽅法:reverse(begin(v), end(v));
string
可以使用push_back(‘a’);
和pop_back();
移除一个字符 // 头文件 #include <string>
is_permutation
可以用来判断v2
是否是v1
的⼀一个序列,返回值是0
或1
// 头文件 #include <algorithm>
,使用方法:is_permutation(v1.begin(), v1.end(), v2.begin());
判断是否是不降序列: is_sorted(begin(a), end(a));
next_permutation()
和 prev_permutation()
// 头文件#include <algorithm>
,使用方法如下:
string s = "12345";
do {
cout << s << endl;
}while(next_permutation(s.begin(), s.end()));
do {
cout << s << endl;
}while(perv_permutation(s.begin(), s.end()));
fill
在#include <algorithm>
头文件中,使用方法:fill(e[0], e[0] + 500 * 500, inf);
大数组必须开全局~
set
中可以使用rbegin
访问最后一个元素,使用方法:cout << *s.rbegin();
getline(cin, s);
前面如果有换行的输⼊入,一定要在前面加上getchar();
(⽤用来读取空格),否则会直接只读入要读的字符串前面的\n
~
关于质因数的解释:质因数就是一个数的约数,并且是质数。除了1以外,两个没有其他共同质因⼦子的
正整数称为互质。因为1没有质因子, 1与任何正整数(包括1本身)都是互质。任何正整数皆有独⼀无二的质因子分解式。只有一个质因子的正整数为质数。
queue q
只有q.push(i);
和 q.pop();
,不是 push_back
和 pop_front
…但是可以 q.back();
和 q.front();
访问队列q的最后一个元素和第一个元素~
// vector的erase与insert要⽤用迭代器器:
vector<int> v{1, 2, 3, 4};
v.insert(v.begin()+2, 5); // {1, 2, 5, 3, 4}
v.insert(v.begin(), 2, -1); // {-1, -1, 1, 2, 5, 3, 4}
// string的erase和insert⽆无须迭代器器:
string s;
s.insert(0, “3”) // 第⼀一个参数为0~s.length()包括len
s.insert(0, 2, “3”);
s.erase(0, 5); // 下标0开始,删除5个字符
s.erase(2); // 删除下标2开始的所有字符
switch()
括号里面只能是int
型或者 char
型
在 string s
后面加字符 s += b;
在 string s
前面加字符 s = a + s;
// a可以是 char
型也可以是 string
类型
substr
只有两种⽤用法:string s2 = s.substr(4);
// 表示从下标4开始一直到结束string s3 = s.substr(5, 3);
// 表示从下标5开始, 3个字符
如果树的某个结点是 NULL
,而 NULL
是没有值val
的,所以不能够 NULL->val
更不能 NULL->next
,否则会导致 runtime error
,可以利用&&
和||
的短路的性质 ,把NULL
的判断提前~
// 求数组中最⼤大值,可以使⽤用algorithm头⽂文件中的max_element(),返回的是指针
int *b = max_element(a, a + 5);
cout << *b;
建立一个 node
结构体,在里面重载小于号,将里面的node
按照cnt
从大到小排列,如果cnt
相等
就按照 value
从å
小到大排列:
struct node {
int value, cnt;
bool operator < (const node &a) const {
return (cnt != a.cnt) ? cnt > a.cnt :value < a.value;
}
};
// 关于并查集(Union Find)的笔记整理理
// 并查集中findFather⾥里里压缩路路径的代码
int findFather(int x) {
int t = x;
while(x != fa[x])
x = fa[x];
while(t != fa[t]) {
int z = t;
t = fa[t];
fa[z] = x;
}
return x;
}
// 并查集的并(Union)代码:
void Union(int a, int b) {
int faA = findFather(a);
int faB = findFather(b);
if(faA != faB) fa[faA] = faB;
}
// 统计有多少个团体(numTrees),每个团体多少⼈人(cnt[i]),⼀一共有多少个⼈人(numBirds)
for(int i = 1; i <= maxn; i++) {
if(exist[i] == true) {
int root = findFather(i);
cnt[root]++;
}
}
int numTrees = 0, numBirds = 0;
for(int i = 1; i <= maxn; i++) {
if(exist[i] == true && cnt[i] != 0) {
numTrees++;
numBirds += cnt[i];
}
}
// sscanf() 从⼀一个字符串串中读进与指定格式相符的数据
// sprintf() 字符串串格式化命令,主要功能是把格式化的数据写⼊入某个字符串串中
// 在头⽂文件 #include <string.h>,使⽤用⽅方法:
char a[50], b[50];
double temp;
sscanf(a, "%lf", &temp);
sprintf(b, "%.2lf",temp);
如果觉得数组从下标0开始麻烦,可以考虑舍弃0位,从1下标开始存储数据~
如果对于不同的结果输出不同的字母,最好考虑用字符数组或者字符串数组将这些字符预先存储好,
避免多个printf语句句导致的错误率增加~ string str[10] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
处理理最后一个结果末尾没有空格的情况,考虑在for语句里面添加if语句的方法处理:if(i != n - 1) printf("");
如果输入一组数据和输入好多个组数据的得到的同一组答案不⼀一样,考虑下是不是int n后忘记写了输入语句…
// 把字符串串string变成字符数组char *c,在头⽂文件<string>⾥里里⾯面
string s = "abc";
char *c = s.c_str();
如果name
为10个字符,注意 char name[]
要开至少11个字符~
string.find
返回的是下标的值,如果没有,用== string::npos
,如果找到了某个字符,就将这个字符的位置赋值给index
,可以这样写代码:
if(s.find('a', 5) != string::npos)
int index = s.find('a', 5);
解释:int index = s.find('a');
//返回a这个字⺟母第一次出现的下标(从0开始)int index = s.find('a', 5);
//从下标5开始,寻找a第一次出现的下标
按照名称的升序排序,因为 strcmp
比较的是 ACSII
码 ,所以 A < Z
。写cmp
函数的时候 return strcmp(a.name, b.name) <= 0;
,因为 cmp
的 return
语句需要返回的是 true
或者false
的值,所以要写<= 0
这样的形式。比较ACSII
码 的大小, strcmp('a', 'z')
返回负值,因为 a<z
,a - z < 0
不能 char *str; puts(str);
必须 char str[100000];puts(str);
bool
变量在 main 函数里面记得要初始化, bool flag[256] = {false};
在main函数外面(全局变量量)会
被自动初始化为false
就可以不写~
unordered_map
和 multimap
:头文件分别为 #include <unordered_map>
和#include <map> unordered_map
是不排序的map
,主要以key
,下标法来访问。
在内部unordered_map
的元素不以键值或映射的元素作任何特定的顺序排序,其存储位置取决于哈希值允许直接通过其键值为快速访问单个元素(所以也不是你输入的顺序,别想太多…)multimap
是可重复的map,因为元素可重复,所以一般用迭代器遍历
段错误:数组越界,访问了非法内存 段错误也有可能是因为数组a没有初始化,导致 b[a[2]]
这种形式
访问了非法内存
vector<int> v[n]
——建立一个存储int
类型数组的数组,即n行的二维数组;也可以用vector<vector<int>> v
建立二维数组~
指针是个unsigned int
类型的整数~
两个 int
类型的指针相减,等价于在求两个指针之间相差了几个int
。如 &a[0]
和 &a[5]
之间相差了5个int,会输出5~
浮点数的比较不能直接用 ==
比较,因为根据计算机中浮点数的表示方法,对于同一个小数,当用不
同精度表示时,结果是不一样的,比如 float
的 0.1
和 double
的 0.1
,直接用==
比较就会输出不相等,所以可以使用自定义 eps = 1e-8
进行浮点数的比较:
const double eps = 1e-8;
#define Equ(a, b) ((fabs((a) - (b))) < (eps))
#define More(a, b) (((a) - (b)) > (eps))
#define Less(a, b) (((a) - (b)) < (-eps))
#define LessEqu(a, b) (((a) - (b)) < (eps))
if(Equ(a, b)) {
cout << "true";
}
// 使⽤用while接收输⼊入的两种⽅方式
while(scanf("%d", n) != EOF) {
}
// 等价于下⾯面这种:
//因为EOF⼀一般为-1,所以~按位取反-1正好是0,就可以退出循环了了
//所以也写成下⾯面这种情况
while(~scanf("%d", &n)) {
}
//此处有的⼩小可爱会问我,为什什么⾃自⼰己的命令⾏行行写这样的代码就会得不不到输出结果,但是OJ却能AC,因为计算机⼀一直在等你的输⼊入结束呀,你要在输⼊入完所有数据之后按输⼊入结束符(ctrl+d还是ctrl+c不不同操作系统不不⼀一样…)它才知道你已经输⼊入结束了了才会输出你的结果呀~但是提交到OJ就不不⼀一样啦,他会⾃自⼰己判断输⼊入⽂文件有没有已经读取完,所以在OJ上直接能AC~
queue
、stack
、priority_queue
是用 push
和 pop
vector
、 string
、deque
是用push_back
push
和pop
只是一个动作,而queue
是用front
和back
访问第一个和最后一个元素的,stack
使
用 top
访问最上面的一个元素
stack
、 vector
、 queue
、set
、 map
作为容器,所以都有 size
传参是拷贝,所以用引用的话更快,传参拷贝可能会超时~
%c
是会读入空格和回车的,如果真的想要获取到输入中的字符%c
,可以通过在scanf
⾥里面手动添加空格的方式避免:scanf("%d %c %d", &a, &b, &c);
在 #include <algorithm>
头文件里面,有 reverse
函数,reverse(s.begin(), s.end());
, reverse
是直接改
变字符串本身的,并没有返回值,不能 reverse
之后赋值给一个字符串,所以string t = reverse(s.begin(),s.end());
这样是不对的~
使用 fill
初始化二维数组是 fill(e[0], e[0] + 510 * 510, 99999);
,不是fill(e, e + 510 * 510, 99999);
,因为⼆维数组在内存里面存储是线性的连续的一段…从 e[0]
的内存开始一直到n * n
大小的内存~
循环无限输出考虑一下是不是 i--
写成了 i++
容器 vector
、 set
、map
这些遍历的时候都是使用迭代器访问的, c.begin()
是一个指针,指向容器的第一个元素, c.end()
指向容器的最后一个元素的后一个位置,所以迭代器指针it
的for
循环判断条件是it != c.end()
,我再重复⼀一遍~ c.end()
指向容器的最后一个元素的后一个位置,这是一个重点和难点,我画个图加深一下小可爱们的记忆(再biu你们一下, biubiubiu~这下总该记得了吧~)
来源:CSDN
作者:sharetown
链接:https://blog.csdn.net/cyhk24/article/details/104535584