【摘出其中有用的片段,便于随时查看!】【未完待续】
进度:
完成章节(1,3,7,8,9)
第二章还剩指针 & 引用;
第六章剩 最后一节
第八章只有一点BFS,加入图一章吧!
第十章 - 图 (遍历完成)
目录:
书本内容
第4章对常用的基本算法思想进行介绍,非常重要,建议读者多花一些时间仔细思考和训练;
推荐使用方法:阅读一节本书内容,然后做一节习题集对应小节的题目!
推荐使用的编译器: Dev-C++、C-Free、Code::Blocks,而VS系列是较为厚重的编译器,不建议(博主用的VS Code,感觉很漂亮!而且对于Python和前端的支持很好啊!)
| 如何高效做题
符号常量 & const常量:
#define 标识符 常量
const 数据类型 变量名 = 常量;
两种方法都可用,推荐const写法!
题外话:
define
除了可以定义常量外,还可定义任何语句或片段
//其中的括号必须加,因为宏定义是直接将对应部分替换,然后进行编译和运行的
#define ADD(a, b) ((a) + (b))
int main() {
int num1 = 3, num2 = 5;
printf("%d", ADD(num1, num2));
return 0;
}
数据类型:
类型 | 取值范围 | 大致范围 |
---|---|---|
int | -2^31 ~ 2^31 - 1 | -2 x 10^9 ~ 2 x 10^9 |
long long | -2^63 ~ 2^63 - 1 | -9 x 10^18 ~ 9 x 10^18 |
float | -2^128 ~ 2^128 | 实际精度 6 ~ 7位 |
double | -2^1024 ~ 2^1024 | 实际精度 15 ~ 16位 |
char | -128 ~ 127 | -128 ~ 127 |
bool | 0(false) or 1(true) |
int:
-2^31 ~ 2^31 - 1
,可记住绝对值在10^9范围以内(或说32位)的整数都可以定义为int型;long long:
浮点型:(对于浮点型来说,尽量都用double来存储)
scanf
用%lf,
printf
用%f
)字符:
\n
代表 换行\0
代表 空字符NULL,其ASCII码为0,注意\0
不是空格bool:
位运算符:
无穷大数INF可以设置成(1<<31) - 1(必须加括号,因为 位运算符的优先级没有算术运算符高),但一般更常用的是2^30 - 1,这样可以避免相加溢出,
const int INF = (1 << 30) - 1
const int INF = 0x3fffffff
//二进制表示,两者等价
六种位运算符整理
运算符 | 含义 | 语法 | 效果 |
---|---|---|---|
<< | 左移 | a << x | 按二进制位 左移 x 位 |
.>> | 右移 | a >> x | |
& | 位与 | a & b | 按二进制对齐,按位进行与运算 |
I | 位或 | a I b | |
^ | 位异或 | a ^ b | 按二进制对齐,按位进行异或 |
~ | 位取反 | ~a |
scanf("%d:%d:%d", &hh, &&mm, &&ss);
%d
& %s
(空格或换行);%c
可以读入空格或换行 %d
和%c
时需要加空格,即:int n;
char c;
scanf("%d%c", &n, &c); //会把空格读入,产生错误
scanf("%d %c", &n, &c); //能正确读入
%
或/
,需要在前面再加一个%
或 /
%md
: 可使不足m位的int变量以m位进行右对齐输出,其中高位用空格补齐,若本身超过m位,则保持原样%0md
:不同在于 变量不足m位时,将在前面补0而非空格%.mf
:可让浮点数保留m位小数输出(四舍六入五成双)getchar & putchar
输入 或 输出 单个字符(包括空格、换行、Tab)
getline()
//读入char型数组中:
char str[100];
cin.getline(str, 100);
//读入string容器
string str;
getline(cin, str);
// 输出换行,以下两种都可以
cout << "\n" << endl;
允许大小:
如果数组大小较大 (大概10 ^ 6 级别),则需要将其定义在主函数外面,否则会使程序异常退出;原因是函数内部申请的局部变量来自系统栈,允许申请的空间较小;而函数外部申请的全局变量来自静态存储区,允许申请的空间较大。
统一赋值
memset(数组名, 值, sizeof(数组名);
需要在开头添加头文件string.h
且只建议初学者使用memset
赋值0
或-1
,如果要对数组赋其他数字,那么请使用fill()
函数(但memset
的执行速度更快)
注: 对二维数组或多为数组的赋值方法也是一样的(仍然只写数组名),不需要该改变任何东西
字符数组
可以通过直接赋值字符串来初始化(仅限于初始化,程序其他位置不允许这样直接赋赋值整个字符串)
eg: char str[15] = "Good Story!";
gets() & puts()
gets:输入一行字符(注:gets
识别换行符\n
作为结束)【因此使用scanf
后,使用gets
需要先用getchar()
吸收换行符】
puts:用来输出一行字符串,并紧跟一个换行
字符数组的存储方式
\0
,以表示存储的字符串的结尾;\0
在使用gets
或scanf
输入是会自动添加在输入的字符串后面,并占用一个字符位,而puts
与printf
就是通过识别\0
作为字符串的结尾来输出的;\0
的ASCII码为0
,占用一个字符位,因此开字符数组时其长度必须要比实际长度至少多1
;【\0
和空格不是不是用一个东西,空格的ASCII码是32,切勿混淆】int
型数组的末尾不需要加\0
,只有char
型数组需要;scanf
函数的%s
格式或gets
函数输入字符串(例如使用getchar
),请一定在输入的字符串后加入’\0
‘,否则puts
和 printf
会因为无法识别字符串末尾而输出一大堆乱码。char str[15];
for (int i = 0; i < 3; i++) {
str[i] = getchar();
}
puts(str);
//输入: T^T
//输出: T^T腫?w?@
头文件string.h
下的常用函数(字符数组)
strlen(str)
:得到字符数组中第一个\0
前的字符的个数 strcmp(str1, str2)
:比较字典大小strcpy(str1, str2)
:把字符数组str2
复制给str1
,包括结束符\0
strcat(str1, str2)
:把字符数组str2
接到 str1
后面sscanf & sprintf
sscanf
:把字符数组str
中的内容以%d
的格式写到n
中sprintf
:把n
以%d
的格式写到str
字符数组中 sscanf
还支持正则表达式,结合正则表达式,很多问题可以迎刃而解】sscanf(str, "%d", &n);
sprintf(str, %d", n); //参数顺序一样,区别只在于n前是否取地址
int n;
double db;
char str[100] = "2048:3.14,hello", str[200];
sscanf(str, "%d:%.2f,%s", &n, &db, str2);
printf("n = %d, db = %.2f, str2 = %s\n", n, db, str2);
// 输出: n = 2048, db = 3.14, str2 = hello
int n = 12;
double db = 3.1415;
char str1[100], str2[100] = "good";
sprintf(str, "%d:%.2f,%s", n, db, str2);
printf("str = %s\n", str);
//输出: str = 12:3.14,good
*
*
*
*
*
malloc
& new
)malloc
函数: C 中,头文件stdlib.h
new
运算符:C++ 中 (推荐使用)
malloc函数 & free函数
C中用于申请动态内存的函数,返回类型是申请的同变量类型的指针,申请失败则返回空指针NULL
;用法:
int* p = (int*)malloc(sizeof(int));
node* p = (node*)malloc(sizeof(node));
/* 含义:
以需要申请的空间大小sizeof(node)为malloc函数的参数,
这样malloc函数会向内存申请一块大小为sizeof(node)的空间,并返回指向这块空间的指针,
但此时该指针是个位确定的指针void*,因此需要把它强制转换为node*型的指针
*/
new运算符 & delete
new
是C++中用来申请动态空间的运算符,
其返回类型同样是申请的同变量类型的指针,但申请失败 会启动C++异常机制处理而不是返回空指针,没有特殊处理的情况下会直接退出程序,用法:
int* p = new int;
node* p = new node;
内存泄露
指使用malloc
与new
开辟的内存空间在使用过后没有释放,导致其在程序结束之前始终占据该内存空间,这在大程序中很容易导致内存耗尽
释放内存
free
函数(对应malloc
,成对出现)
用法: free(p)
- - P
为需要释放的内存空间的指针变量
效果:
1、释放指针变量P
所指向的内存空间
2、将指针变量P
指向空地址NULL
delete
运算符
用法和效果均与free
相同
注: 一般考试中,分配的空间在程序结束时即被释放,因此即使不释放空间,也不会产生什么影响(内存大小一般足够一道题使用);但从编程习惯上,读者应养成即时释放空间的习惯!
《算法笔记》为了使算法讲解更侧重于对思路的讲解,故在代码中没有释放空间
初始化
当然可以通过逐项赋值,但是当结构体内部变量较多时,不太方便,这时建议使用构造函数进行赋值。
结构体内部有一个默认的构造函数,但不可见;由于这个构造函数的存在,我们才可以不进行初始化就定义结构体变量,因为它没有让用户提供任何初始化参数。
struct student {
int id;
char gender;
//用以不初始化就定义结构体变量(否则不能不初始化)
student() {}
//只初始化gender属性
student(char _gender) {
gender = _gender;
}
//同时初始化id和gender
student(int _id, char _gender) {
id = _id;
gender = _gender;
}
};
注意:自己重新定义了构造函数,则不能不经过初始化就定义结构体变量,即默认的结构体被覆盖了。可以手动把原有的默认构造函数加上,这样就既能不初始化就定义结构体变量,又能享受初始化带来的便利。
/*写在结构体内*/
//价格高的优先级高( 正好与排序函数sort中的cmp效果相反 )
struct fruit {
string name;
int price;
friend bool operator < (fruit f1, fruit f2) {
return f1.price < f2.price;
}
};
//之后在优先队列中就可以直接定义使用了,其内部已满足价格高的优先级高
priority_queue q;
-----------------------------------------------------
/*写在结构体外*/
struct fruit {
string name;
int price;
}f1, f2, f3;
//把小于号改成一对小括号,并将重载的函数用struct包装起来
struct cmp {
bool operator () (fruit f1, fruit f2) {
return f1.price > f2.price;
}
};
//定义时也有变化,把greater<>部分 换成 cmp
priority_queuevector, cmp > q;
/* 注意:
/* 重载大于号会编译错误,因为从数学上来说只需要重载小于号,
/* 即 f1>f2 等价于 f2
若结构体内的数据较为庞大(例如出现字符串或数组),建议使用引用来提高效率,此时比较类的参数中需要加上 const
和 &
:
friend bool operator < (const fruit &f1, const fruit &f2) {
return f1.price > f2.price;
}
bool operator () (const fruit &f1, const fruit &f2) {
return f1.price > f2.price;
}
对于一般的OJ系统来说,一秒能承受的运算次数大概是10^7 ~ 10^8
,因此,O(n^2)
的算法当n
的规模为1000
时是可以承受的,而100000
时则不可承受。
PAT为单点测试
多点测试:
即需程序一次运行所有数据,故要程序可反复执行代码的核心部分。
//scanf()函数的返回值是其成功读入的参数个数,
//而读取失败时会返回-1(C语言中EOF表示-1)
while(scanf("%d", &n) != EOF){
……
}
//若为输入为字符串,也可用gets()判断
while(scanf("%s", str) != EOF)
while(gets(str) != NULL)
对于main()
函数来说,返回0
的意义在于告知系统程序正常结束。
常用 math 函数
fabs(double x)
: 取绝对值
floor(double x)
ceil(double)
: 向下向上取整
pow(double r, double p)
: 返回 r ^ p,要求 r 和 p 都是double型
sqrt(double)
:double型变量的算术平方根
round(double x)0
:四舍五入
log(double x)
:ln(x)
sin()
cos()
tan()
asin()
acos()
atan()
:三角函数
浮点数的比较
如果没有经过容易损失精度的运算,就不需考虑误差,可直接比较;但当有这种操作时,精度的损失就不可忽视,需要引入极小数eps对误差进行修正;
eps一般取
10^-8
,不会导致误判或漏判
判等 a == b
:如果a落在[b - eps, b + eps]中,就应当判等
判大于等于 a >= b
:在大于的基础上,把误差从eps
变为 -eps
,包含进了等于的情况
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 MoreEqu(a, b) (((a) - (b)) > (-eps)) // >=
#define LessEqu(a, b) (((a) - (b)) < (eps)) // <=
补充说明:
1、由于精度问题,在经过大量运算后,可能一个变量中存储的0
是很小的负数,这时如果对其开根号sqrt()
,就会出错。同样的问题还出现在asin(x)
当x
存放+1
、acos(x)
当x
存放-1
时。这种情况需要eps
保证其值在定义域内。
2、在某些由编译环境产生的原因下,本应为0.00
的变量在输出是会变成-0.00
。这个问题是编译环境自身的bug,只能把结果存放在字符串中,然后与-0.00
进行比较,如果比对成功,加上eps
来修正为0.00
。
圆周率π
写成常量acos(-1.0)
即可
(单独整理在一篇博客里了)
*
*
*
* 整理自《算法笔记》!!