给1~n的数,要求每13个分为一类,问某一数num是第几类中的第几个数:例如扑克牌113,1426,这样分类的数字,求第几类:用num / 13的方法,要考虑当num == 13的时候其实还是在第一个类别里面,但却由0变成了1。解决办法是:num = num – 1后,这样使得num变成了012,1325,…再进行num / 13,正好符合除以得到的结果分类。求是第几个数:用num % 13 ,不需要先num = num – 1。需要的是num % 13 + 1,因为取余得到的结果是0~12,所以要在最后取余后的结果之后加1满足条件。
如果觉得数组下标0开始麻烦,考虑舍弃0位,从1下标开始存储数据。
如果对于不同的结果输出不同的字母,最好考虑字符数组或者字符串数组预先存储好,避免多个printf语句导致的错误率增加。
处理最后一个结果末尾没有空格的情况,考虑在for语句里面添加if语句的方法处理:if(i != n - 1) printf(" ");
如果输入一组数据和输入好多个组数据的得到的同一组答案不一样,考虑下是不是int n后忘记写了输入语句。。。(mdzz。。。)
substr只有两种用法:
关于pair:
typedef pair<string, int> p;
vector<p> v;
p temp = make_pair("abc", 123);
v.push_back(temp);
cout << temp.first << " " << temp.second << endl;
cout << v[0].first << " " << v[0].second << endl;
string s = “abc”;char *c = s.c_str();
把字符串变成字符数组,在头文件
里面
map映射find函数:
map和set都是有find函数的,用的时候so easy:
if(m.find(“ABC”) != m.end())
cout << “can found it”;
abs()在头文件stdlib.h里面
find方法:
#include
string返回的是下标的值,如果没有,用 == string::npos
int index = s.find(‘a’); //返回a这个字母第一次出现的下标(从0开始)
int index = s.find(‘a’, 5); //从下标5开始,寻找a第一次出现的下标
如果找不到,会出现一个随机值,可以这么写:if(s.find(‘a’, 5) != string::npos)
int index = s.find(‘a’, 5);
#include
#include
按照名称的升序排序,因为strcmp比较的是ACSII码,所以A < Z。写cmp函数的时候return strcmp(a.name, b.name) <= 0;
return语句返回的是true或者false的值,所以要写 <= 0 这样的形式。比较ACSII码的大小,strcmp(‘a’, ‘z’)返回负值,因为a
如果name为10个字符,注意char name[]要开至少11个字符。
不能char *str; puts(str);
必须char str[100000]; puts(str);
#include
string str1;
char str2[100];
cin >> str1;
cin >> str2;//cin以空格和回车为结束符
char ch;
cin.get(ch);//用来接收一个字符
char str[20];
cin.get(str, 20);//接收一行字符串,可以接收空格
char str[20];
cin.getline(str, 20);//接收一行字符串,可以接收空格
cin.getline(str, 20, '#');//接收一行字符串,可以接收空格,判断直到遇到'#'为止
#include
string str1;
char str2[100];
cin >> str1;
cin >> str2;//cin以空格和回车为结束符
char ch;
cin.get(ch);//用来接收一个字符
char str[20];
cin.get(str, 20);//接收一行字符串,可以接收空格
char str[20];
cin.getline(str, 20);//接收一行字符串,可以接收空格
cin.getline(str, 20, '#');//接收一行字符串,可以接收空格,判断直到遇到'#'为止
bool变量在main函数里面记得要初始化,bool flag[256] = {false}; 在main函数外面会被自动初始化为false~
unordered_map和multimap
vector
——建立一个存储int类型数组的数组,即二维数组
段错误:数组越界,访问了非法内存
将n转化为d进制,最后需要将arr数组倒置
do{
arr[len++] = n % d;
n = n / d;
}while(n != 0);
判断是否为素数
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;
}
vector可以pop_back()删除最后一个元素
multiset在头文件set里面
如果有将近一半的答案错误,多半是输出语句里面的字母大小写写错了~
define不要加分号啊啊啊啊否则会编译失败而且是在程序里面失败啊啊啊expected expression
用了strlen一定要记得写头文件cstring(也就是string.h)
index、swap、value、count、now、cmp、friend是关键字不能重复定义。(xcode)
段错误也有可能是因为数组a没有初始化,导致b[a[2]]这种形式访问了非法内存
scanf的%c可以读入空格和换行
如果程序在输入数据之后异常退出,要考虑是不是scanf使用的时候漏写&
如果程序在输入数据之后异常退出,要考虑是不是scanf使用的时候漏写&
如果程序在输入数据之后异常退出,要考虑是不是scanf使用的时候漏写&
scanf(“%s”, str); str是一个字符数组,str名表示了指针所以这个不用写&
double类型变量,输出格式是%f,scanf中却是%lf
%5d
占5位,右对齐格式
%05d
占5位,不足前面补0——在某些情况下非常有用
getchar()
用来输入单个字符(包括空格和换行符),putchar()
用来输出单个字符
里面的函数:
fabs(double x)
取绝对值floor(double x)
向下取整ceil(double x)
向上取整pow(double r, double p)
r和p都是double类型sqrt(double x)
x是double类型log(double x)
自然对数为底的对数(ln x),C语言中没有对任意底数求对数的函数,用换底公式才行sin(double x)
cos(double x)
tan(double x)
pi = acos(-1.0)
asin(double x)
acos(double x)
atan(double x)
round(double x)
四舍五入,传入double型返回double型,最后要进行取整(5.123456 = 5.0000)memset(数组名, 值, sizeof(数组名));
memset只能赋值0或者-1,因为memset是按字节赋值的。(对二维数组和多维数组赋值也一样,只要写数组明即可)。memset在头文件string.h
里面。如果要赋值其他数字(如1),那么用fill函数
memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。
可以用作int型数组(只能0或者-1),也可以用作char型数组(记得sizeof(char) * (n – 1))
不推荐使用memeset,推荐使用更好用的fill函数,在头文件algorithm里面
scanf中的%s识别空格作为字符串的结尾
gets
用来输入一行字符串,以\n为输入结束。所以scanf完一个整数后,如果要使用gets,必须先getchar();
puts(str);
用来输出一行字符串并且自动加上\n
头文件
strlen(str)
返回不包括\0
的长度strcmp(str1, str2)
按字典序比较(比较的是ASCII,大写字母在前,小写字母在后)。<返回负数(不一定是-1),==返回0,>返回正数(不一定是1)strcat(str1, str2)
把str2接到str1后面sscanf(str, "%d", &n);
把字符数组str里面的内容以%d形式输入到变量n里面//sscanf还支持正则表达式sprintf(str, "%d", n);
把n以%d的形式写到str字符数组中
指针是个unsigned类型的整数
两个int类型的指针相减,等价于在求两个指针之间相差了几个int。如&a[0]和&a[5]之间相差了5个int,会输出5
用指针进行swap: 传入地址,子函数参数定义为指针int *a
和int *b
,使用int temp交换a和b中的内容(*a
和*b
)
void swap1(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
swap1(&a, &b);
用引用进行swap:传入a和b,子函数参数定义为引用,用int temp交换a和b
void swap1(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
swap1(a, b);
swap函数定义在了using namespace std;
里面 ,无需特殊的头文件。为了避免冲突请自己写的时候用swap1
结构体中元素的访问,->和.访问的区别:
struct node stu, *p;
stu.id = 1001;
(*p).id = 1001;
//为了简化结构体指针变量的(*p).的复杂写法,又可以用p->id来直接访问。
p->id = 1001;
//也就是说,使用->的前面是一个指向结构体的指针变量p,.的前面是一个结构体变量stu
使用自定义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";
}
时间复杂度:O(1) < O(log n) < O(n) < O(nlogn)< O(n^2)< O(n^3)< O(n^k)< O(2^n)
输入的具体个数不明确
while(scanf("%d", n) != EOF) {
}
//因为EOF一般为-1,所以~按位取反-1正好是0,就可以退出循环了
//所以也写成下面这种情况
while(~scanf("%d", &n)) {
}
要求最后一组数据没有空格:
while(T--) {
if(T > 0)
printf(" ");
}
for(int i = 0; i < n; i++) {
if(i < n - 1)
printf(" ");
}
sort对于vector也可以sort(v.begin() + 1, v.end()-1);
这种取第2个到倒数第二个数排序的形式
friend也是关键字啊啊啊(xcode适用)
queue、stack、priority_queue是push和pop
vector、string、deque是push_back
push和pop只是一个动作,而queue是用front和back访问第一个和最后一个元素的,stack使用top访问最上面的一个元素的
stack、vector、queue、set、map作为容器,所以都有size
//根据所给序列构建一个二叉搜索树
#include
#include
using namespace std;
struct node {
int v;
struct node *left, *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;
}
int main() {
int n, t;
scanf("%d", &n);
node *root = NULL;
for(int i = 0; i < n; i++) {
scanf("%d", &t);
root = build(root, t);
}
return 0;
}
计算sort的区间,是(a.begin(), a.begin() +n);
的形式,因为 n – 0 = n个数字,所以要使中间多n个数字就要+n,a.end()是最后一个数字的后一位,也就是说,第二个参数应该是要排序序列的最后一个元素的后一位~~
从字符数组的下标1开始赋值,从下标1开始输出
#include
#include
using namespace std;
int main() {
char a[100];
scanf("%s", a + 1);
printf("print a:%s\nprint a+1:%s\n", a, a+1);
int lena = strlen(a+1); //从a+1开始计算str的长度
printf("strlen(a+1) = %d", lena);
return 0;
}
/*
1234
print a:
print a+1:1234
strlen(a+1) = 4
*/
素数表的建立
vector<int> prime(500000, 1);
for(int i = 2; i * i < 500000; i++)
for(int j = 2; j * i < 500000; j++)
prime[j * i] = 0;
在scanf接收完后一定要getchar();之后再使用getline(cin, s); 否则getline得到的是空字符。getline与getline之间无需使用getchar(); 可以在scanf完一个整数后后面接着的字符串使用getline,接收数字后面跟着的同一行剩下的字符串~~~
传参是拷贝,所以用引用的话更快,传参拷贝可能会超时~
swap函数在using namespace std;里,不需要头文件
%取余和除号/的优先级等同,优先级等同的时候从左到右运算~~~
%c是会读入空格和回车的,可以通过在scanf里面手动添加空格的方式避免。scanf(“%d %c %d”, &a, &b, &c);
在algorithm头文件里面,有reverse函数,reverse(s.begin(), s.end()); reverse(v.begin(), v.end()); 直接改变字符串本身,没有返回值,不能通过赋值string t = reverse(s.begin(), s.end()); 这样是不对的
dijkstra千万不要先把起点标记为true(已确定)。。因为找最小的边是找的所有false(不确定)的结点中距离最小的。。。
使用fill初始化二维数组是fill(e[0], e[0] + 510 * 510, inf);。。。。不是e,e + 510 * 510。。。。。因为二维数组在内存里面存储是线性的连续的一段。。。从e[0]的内存开始一直到n * n大小的内存。。。
循环无限输出考虑一下是不是i–变成了i++
判断一个图的连通分量数
e[u][v]
可达的结点,中的未被访问的结点 &&关系)深度优先遍历树的层序遍历
最大连续子序列和
sum为要求的最大和,temp为临时最大和,left和right为所求的子序列的下标,tempindex标记left的临时下标。temp = temp + v[i],当temp比sum大,就更新sum的值、left和right的值;当temp < 0,那么后面不管来什么值,都应该舍弃temp < 0前面的内容,因为负数对于总和只可能拉低总和,不可能增加总和,还不如舍弃;舍弃后,直接令temp = 0,并且同时更新left的临时值tempindex。因为对于所有的值都为负数的情况要输出0,第一个值,最后一个值,所以在输入的时候用flag判断是不是所有的数字都是小于0的,如果是,要在输入的时候特殊处理。
#include
#include
using namespace std;
int main() {
int n, flag = 0, sum = -1, temp = 0, left = 0, right = 0, tempindex = 0;
scanf("%d", &n);
vector<int> v(n);
for(int i = 0; i < n; i++) {
scanf("%d", &v[i]);
if(v[i] >= 0)
flag = 1;
temp = temp + v[i];
if(temp > sum) {
sum = temp;
left = tempindex;
right = i;
} else if(temp < 0) {
temp = 0;
tempindex = i + 1;
}
}
if(flag == 0)
printf("0 %d %d", v[0], v[n - 1]);
else
printf("%d %d %d", sum, v[left], v[right]);
return 0;
}
根据前序和中序输出后序
root为前序中当前的根结点的下标,start为当前需要打印的子树在中序中的最左边的下标,end为当前需要打印的子树在中序中最右边的下标。
递归打印这棵树的后序,递归出口为start > end。i为root所表示的值在中序中的下标,所以i即是分隔中序中对应root结点的左子树和右子树的下标。
先打印左子树,后打印右子树,最后输出当前根结点pre[root]的值。
#include
using namespace std;
int pre[] = {1, 2, 3, 4, 5, 6};
int in[] = {3, 2, 4, 1, 6, 5};
void post(int root, int start, int end) {
if(start > end)
return ;
int i = start;
while(i < end && in[i] != pre[root]) i++;
post(root + 1, start, i - 1);
post(root + 1 + i - start, i + 1, end);
printf("%d ", pre[root]);
}
int main() {
post(0, 0, 5);
return 0;
}
已知后序与中序输出前序(先序):
因为后序的最后一个总是根结点,令i在中序中找到该根结点,则i把中序分为两部分,左边是左子树,右边是右子树。因为是输出先序(根左右),所以先打印出当前根结点,然后打印左子树,再打印右子树。左子树在后序中的根结点为root – (end – i + 1),即为当前根结点-右子树的个数。左子树在中序中的起始点start为start,末尾end点为i – 1.右子树的根结点为当前根结点的前一个结点root – 1,右子树的起始点start为i+1,末尾end点为end。
#include
using namespace std;
int post[] = {3, 4, 2, 6, 5, 1};
int in[] = {3, 2, 4, 1, 6, 5};
void pre(int root, int start, int end) {
if(start > end) return ;
int i = start;
while(i < end && in[i] != post[root]) i++;
printf("%d ", post[root]);
pre(root - 1 - end + i, start, i - 1);
pre(root - 1, i + 1, end);
}
int main() {
pre(5, 0, 5);
return 0;
}
树状数组
经典应用:统计序列中在元素左边比该元素小的元素个数
#include
#include
const int maxn = 10010;
#define lowbit(i) ((i) & (-i))
int c[maxn];
void update(int x, int v) {
for(int i = x; i < maxn; i += lowbit(i))
c[i] += v;
}
int getsum(int x) {
int sum = 0;
for(int i = x; i >= 1; i -= lowbit(i))
sum += c[i];
return sum;
}
int main() {
int n, x;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &x);
update(x, 1);
printf("%d\n", getsum(x - 1));
}
return 0;
}
scanf中的%d和%c之间一定要有分隔符的主动scanf输入