《算法笔记》

这本书是一个学长推荐我的,听说对零基础入门很友好,还有配套的刷题网站,这点很重要。
我一直认为,要学好一样东西,最快最直接的方式就是实践,如果只是看书,就会像我高中一样纸上谈兵,空会理论而不会实际的编程,况且多敲代码对于我记忆那些知识点也是很有帮助的。
C语言程序设计我之前是跟着我们学校翁恺老师的慕课学的,把基础的语法点刚看完,看到了指针那块,现在开始看《算法笔记》,就当把之前的知识点都复习一遍了。
而且这本书和传统的C语言学习不同,它介绍的是C和C++的语法,并且它介绍的语法是针对刷题的,并不是面面俱到,这对我这种刚入门的小白比较友好,学了就可以用。

C语言基本知识点

刚入门C语言,见的第一段代码肯定是这样的

1
2
3
4
#include <stdio.h>
int main(){
return 0;
}

在C++里面第一句也可以写成

#include<cstdio>

数据类型

数据存储形式

计算机中,整除采用补码的方式存储,以1个字节(8bit)的char类型为例

为了让正数和负数能直接相加减,负数1-1的二进制为010-1,表现为二进制就是(1)00000000-00000001=11111111,这样子1+1-1+1就能等于0了,这也解释了一个字节的char类型为什么数据范围是-128到127,表现为二进制就是10000000到01111111,

变量类型

名称 说明
int 最常用的整型 4字节 %d
long long 范围更大的整型 8字节
float 浮点型 但精度较小 %f
double 常用的浮点型 精度较高 %lf
char 字符型 %c,1字节
bool 布尔型(在C++里面才能直接用)

注:数据在计算机内存储均是二进制,只是输入输出时格式不同罢了

类型转换

(新类型名)变量名

定义常量

const 数据类型 变量名 = 常量;

数值运算符

+,-,*这三个没什么可说的

/ 这个是整除

% 这个是取余,取余的结果正负只和被取余的正负有关

1
2
3
i++是先调用i后加
++i是先加后调用i
两者就差了个1

关系运算符

运算符 含义
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于(注意是两个等于!
!= 不等于

逻辑运算符

运算符 含义
&&
||
!

条件运算符

A?B:C;

这段代码的含义是如果A为真就返回B否则C

我觉得这个运算符很好用,相当于一个很简洁的判断语句

位运算符

这个我以前从没有接触过,也不知道有什么用,就先记着吧

运算符 含义
<< 左移
>> 右移
& 位与
| 位或
^ 位异或
` 位取反

顺序结构

赋值表达式

变量类型 变量名=初值;

输入

scanf("格式",&变量);

变量前面要记得加&代表地址

数据类型 格式符
int %d
long long %lld
float %f
double %lf
char %c
字符串 %s,此处不用加&

输出

printf("格式",变量);

数据类型 格式符
int %md(m表示不足m位的以m位右对齐),%0md(用0替换之前的空格)
long long %lld
float %f
double %f,%.mf(m表示小数位数)
char %c
字符串 %s

注意double的格式从%lf变成了%f,即输入lf,输出f

也可以用getchar()读入单个字符

用putchar()输出单个字符

注释

//注释

//后的话都不会被执行

数学函数

需要在前面加上

include<math.h>

所有的对象都是double 类型

函数 用法
fabs() 绝对值
floor(),ceil() 均用于double变量,floor向下取整。ceil向上取整
pow(r,p) 返回r^p^
sqrt() 返回算术平方根
log(x) 返回lnx
sin(),cos(),tan() 三角函数
asin(),acos(),atan() 反三角函数
round() 四舍五入,返回值需进行取整

选择结构

if语句

1
2
3
4
5
6
7
if(条件A){
...
}else if(条件B){
...
}else {
...
}

switch语句

1
2
3
4
5
6
7
8
9
10
11
switch(表达式){
case 常数1:
...
break;
case 常数2:
...
break;
...
default:
...
}

循环结构

while语句

1
2
3
while(条件A){
...
}
1
2
3
do{
...
}while(条件A);

个人感觉do while没啥用,你要先执行一遍可以在while语句前写吧

for语句

1
2
3
for(A;B;C){
...
}

A是循环变量赋初值

B是循环条件

C是循环变量改变

如果有确定的循环次数就用for,其他用while

break和continue

break退出当前循环

continue退出本轮循环进入下一轮

数组

数据类型 数组名[数组大小]={初值};

注意下标是从0开始

冒泡排序

相邻两两比较,每轮将最值移动到最左或者右

高中已经学过,这里不作过多讲解

二维数组

数据类型 数组名[第一维大小][第二维大小]

memset

这个函数用于将数组中的每一个元素赋以相同的值

memset(数组名,值,sizeof(数组名));

字符数组

初始化

方法1:和普通数组一样

方法2:

char str[数组大小]=字符串;

输入输出

1
2
3
char str[10];
scanf("%s",str);
printf("%s",str);

getchar()和putchar()和之前相同

gets()和puts()用于输入或输出一行字符串(输入要换行结尾,输出自带换行)

存放方式

字符数组末尾自带一个空字符串\0,占用一个字符位,所以创建字符数组的时候至少要多一位

使用getchar()输入时一定要在末尾输出空字符串

string.h头文件

strlen()

返回字符串长度

strcmp(A,B)

返回A和B字符串大小比较结果,如果A<B则返回一个负整数,如果A=B则返回0,如果A>B则返回一个正整数

strcpy(A,B)

把字符串B复制给A(包括结尾的\0)

strcat(A,B)

把字符串B接到字符串A后面

sscanf和sprintf

1
2
ssanf(str,"%d",&n);
sprintf(str,"%d",n);

上述代码,sscanf将str用%d的形式写入n中,sprintf将n用%d的形式写入str中

这里的%d还可以换成别的形式

函数

1
2
3
返回类型 函数名(参数类型 参数){
函数主体
}

返回类型中写void意思是空,即该函数没有返回值

参数也可以不写,如

1
2
3
void print1(){
printf("hello!\n");
}

常规函数

1
2
3
4
5
int judge(int x){
if(x>0) return 1;
else if(x==0) return 0;
else return -1;
}

全局变量

定义在所有函数之前的变量

后面的函数都可以改变该变量,也可以使用

局部变量

定义在函数内部的变量

只在函数内部生效,函数结束时变量销毁

定义函数时的参数为形参,调用函数时的参数才是实参

所以main()其实也是一个函数,而且返回值为0

以数组为参数

数组为参数时,和普通局部变量不同,在函数中修改数组就是对原数组的修改

且数组不能作为返回类型

嵌套调用

即在一个函数中调用另一个函数

递归

即函数调用自己

指针

指针就是变量的地址

之前scanf函数里面的&符号就是用来显示变量地址的

指针变量

定义时需要加上*

1
2
3
int* p;
double* p;
char* p;

定义多个变量时,*只对第一个变量生效,若需要定义多个指针,则需在变量前都加上

int*p1,*p2,*p3;

p保存的是地址,*p保存的就是该地址的元素

可以对*p直接赋值

1
2
3
int a;
int *p=&a;
*p=233

这样赋值之后a的值也会变成233

指针和数组

对数组a[10]而言,a代表的就是a[0]的地址,即a=&a[0],同理可以推出a+i=&a[i],*(a+i)=a[i]

所以输入数组也可以用下面的代码

scanf("%d",a+i);

同理,输出也可以用下面的代码

printf("%d",*(a+i));

指针和数组是有共同之处的,可以把数组看成特殊的指针

**注意:**以int类型的指针和数组为例,相邻数组地址相差是4(因为int占用4个字节),但对指针而言,+1就相当于+4(因为是int类型的指针)

使用指针作为函数参数

当函数参数为指针类型时,视为将变量的地址传入函数,在函数中改变该地址的值,原先的数据也会改变

也可以利用指针交换两个数

这是传统的交换

1
2
3
4
int a=1,b=2;
int temp=a;
a=b;
b=temp;

这是用指针交换

1
2
3
4
5
void swap(int* a,int* b){
int temp = *a;
*a=*b;
*b =temp;
}

由于函数中的是形参,所以传统方法在函数中是无法使用的,这时就要用到指针,直接去修改对应地址的元素实现交换

引用

由于函数中只能该改编形参,要想改变全局变量就只能用指针,通过引用也可以改变全局变量

语法是在函数的参数前加上&,如

1
2
3
void chang(int&x){
x=1;
}

这样引用该函数时就能将传入的函数改成1

引用指针

1
2
3
4
5
void swap(int*&p1,int*&p2){
int*temp=p1;
p1=p2;
p2=temp;
}

这个函数通过引用指针来改变原指针,达到交换值的效果

结构体

把一些相关的变量放在一起存储

定义

1
2
3
struct Name {
基本数据结构或自定义的数据类型
};

比如

1
2
3
4
5
6
struct student{
int id;
char gender;
char name[20];
char major[20];
}Alice,Bob,stu[1000];

Alice和Bob是两个结构体变量,stu[1000]是结构体数组

1
2
studentAlice;
studentstu[1000];

这样定义也是可以的

访问

Alice.id

Bob.name

这是最常用的,而当结构体里面有指针时

原先的(*p).id可以改成p->id

初始化

stu.id=1;

或者

scanf("%d",&stu.id);

但这样不太方便

结构体默认会生成一个与结构体同名的构造函数student(){}

可以自定义构造函数,如

1
2
3
4
5
studentInfo(int _id, char _gender){
id=_id;
gender=_gender;
}
studentInfostu=studentInfo(10086,'M');//调用构造函数进行赋值

构造函数可以定义多个,用于不同场合,如果自定义了构造函数,就不能不经初始化就定义结构体变量,所以在自定义构造函数之前加上studentInfo(){}就可以不初始化定义变量了。

补充

cin和count

在c++中,输入和输出函数为cin和cout,需要添加头文件(不过这两个速度没有scanf和printf快)

1
2
#include<iostream>
using namespace std;

cin

cin的输入不指定格式,直接写变量名就可以了

1
2
3
4
cin>>n;
cin>>db;
cin>>c;
cin>>n>>db>>c;

如果想要读入一整行,需要使用getling函数

1
2
3
4
5
char str[100];
cin.getline(str,100);
//如果是string
string str;
getline(cin,str);

cout

cout使用和cin类似

cout<<n<<db<<c<<str;

这样输出中间是没有空格的,如果需要的话要自己加上" "

同理,如果需要换行的话也要自己加上"\n”,可以用endl代替\n

浮点数的比较

由于浮点数的不确定性,需要用极小数来比较

通常定义一个eps

const double eps = 1e-8;

如果两数之差小于这个值,就认为他们相等

#define Equ(a,b) ((fabs((a)-(b)))<(eps))

同理,大于即两数差大于该值,小于即两数差小于-eps

两数差大于-eps即大于等于

两数差小于eps即小于等于

圆周率

const double Pi=acos(-1.0);

复杂度

时间复杂度

即执行运算的次数等级,此处等级可理解为无穷大的级数,如2n次和n次是同一级

空间复杂度

即算法要消耗的最大空间

一般情况下空间都是够用的,所以可以牺牲空间来换取时间

编码复杂度

算法思想的深度

黑盒测试

单点测试

即程序只需要执行一组数据即可

多点测试

要求程序能一次运行所有数据,并且运行结果都需要正确才能通过测试

1.while…EOF

读到文件末尾为止

2.while…break

满足某个条件时停止输入

3.while(T–)

给出测试数据的组数


第二章知识点就是这些,虽然照着书上依葫芦画瓢写了一遍但感觉离真正理解还是有一段距离,尤其是不怎么接触到过的指针、结构体

上述是我个人的理解总结的,可能会有错误,希望得到指正!