PAT笔记

以下内容来源于柳神《PAT | 蓝桥 | LeetCode学习路径 & 刷题经验》文章中的部分内容,仅供自己学习使用。若有能帮助到大家的请移步到柳神的博客查阅原版。侵删!

dev中查看 vector 的值:*(&v[0])@3——必须对 v[0]变换,先取地址再取值, @后⾯的数字 3 能让dev显示数组前3个的值,在⽤这个之前要先在编译选项-编译器⾥⾯ -std=C++11

INT_MAXINT_MINLLONG_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。这样倒序保存的数就是⼗进制ab进制下的结果。

// 判断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问题,就是用 pq 两个指针,使用 while语句处理链表问题,链表什么的
vector 存储就好了~

输出语句如果有 is / are、加s / 不加s的区别的话一定要当心,比如 "There is 1 account""There are 2 accounts"是不同的输出语句

移除字符串s的第一个字符: s.earse(s.begin());在字符串s前面增加 n0s.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,包含leftright 两个变量,然后以建立
TREE的二维数组 treevector<TREE> tree[n];,然后将tree[i]leftright赋值为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 类型转intlonglong longfloatdoublelong double // 头文件#include <string>stoistolstollstofstodstold
char c[10]类型转 intlonglong long// 头文件#include <cstdlib>atoiatolatoll
intlonglong longfloatdoublelong doublestring// 头文件#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的⼀一个序列,返回值是01 // 头文件 #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_backpop_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;,因为 cmpreturn语句需要返回的是 true或者false 的值,所以要写<= 0这样的形式。比较ACSII码 的大小, strcmp('a', 'z')返回负值,因为 a<za - z < 0
不能 char *str; puts(str);必须 char str[100000];puts(str);

bool变量在 main 函数里面记得要初始化, bool flag[256] = {false};在main函数外面(全局变量量)会
被自动初始化为false就可以不写~

unordered_mapmultimap:头文件分别为 #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~

浮点数的比较不能直接用 == 比较,因为根据计算机中浮点数的表示方法,对于同一个小数,当用不
同精度表示时,结果是不一样的,比如 float0.1double0.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~

queuestackpriority_queue 是用 pushpop

vectorstringdeque 是用push_back

pushpop只是一个动作,而queue是用frontback访问第一个和最后一个元素的,stack 使
top访问最上面的一个元素

stackvectorqueuesetmap 作为容器,所以都有 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++

容器 vectorsetmap这些遍历的时候都是使用迭代器访问的, c.begin()是一个指针,指向容器的第一个元素, c.end()指向容器的最后一个元素的后一个位置,所以迭代器指针itfor循环判断条件是it != c.end(),我再重复⼀一遍~ c.end()指向容器的最后一个元素的后一个位置,这是一个重点和难点,我画个图加深一下小可爱们的记忆(再biu你们一下, biubiubiu~这下总该记得了吧~)
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值