胡凡算法笔记---第二章 C/C++快速入门

第二章 C/C++快速入门

下面介绍C语言相关内容,先来看一段C语言小程序:

#include <stdio.h>
int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    printf("%d", a+b);
    return 0;
}

这个程序分为两个部分:头文件主函数

1.头文件

在上面的代码中,#include<stdio.h>这一行就是头文件。其中,stdio.h是标准输入输出库,如果在程序中需要输入输出,就需要加上这个头文件。

stdio的全称是standard input output,h就是head的缩写,.h是头文件的文件格式。

既然stdio.h是负责输入输出,那么自然还会有负责其他功能的头文件。例如,math.h负责一些数学函数,string.h 负责跟字符串有关的函数,则只需要在需要使用对应的函数时,将它们的头文件包含到这个程序中来即可。

此外,在C++的标准中,stdio.h更推荐使用等价写法: cstdio,也就是在前面加一个c,然后去掉.h即可。所以#include<stdio.h>和#include<cstdio>的写法是等价的,#include<math.h>和#include<cmath>等价,#include <string.h>和#include <cstring>也等价。

2.主函数

int main(){
  ...
  return 0;
}

上面的代码就是主函数。主函数是一个程序的入口位置,整个程序从主函数开始执行。一个程序最多只能有一个主函数

int a, b;

这句话定义了两个变量a和 b,类型是int 型(简单来说就是整数)。

scanf("%d%d", &a, &b);

scanf用来读入数据,这条语句以%d的格式输入a和 b,其中%d就是 int型的输入输出标识。简单来说,就是把a和 b作为整数输入。

printf("号d",a+b);

printf用来输出数据,这条语句计算a + b并以%d格式输出。上面说过,%d就是int型的输入输出标识,所以就是把a+b作为整数输出。因此这段代码的主函数实现了输入两个数a和 b然后输出a+b的功能。

接下来进入正题,讲解一下C语言中各个需要使用的语法。
声明:下文使用的代码请保存成.cpp文件(即C++文件),然后选择C++语言(或C++)进行提交。由于C++向下兼容C,因此采用这种方式可以尽可能防止一些因C与C++之间的区分而导致的编译错误。

2.1 基本数据类型

2.1.1 变量的定义

变量是值可以改变的量,需要定义后才能使用,定义格式如下:

变量类型 变量名;
变量类型 变量名 = 初值;

定义要求:

  1. 不能是C语言标识符
  2. 变量名的第一个字符必须是字母或下划线,除第一个字符之外的其他字符必须是字母、数字或下划线
  3. 区分大小写

2.1.2 变量类型

1.整型

  1. int 32bit
int num;
  1. long long int 64bit
long long bigNum;
long long bigNum = 1234567890123456LL

%d是int型的输出格式

2.浮点型

  1. 单精度float 32bit
float f1;
float f1 = 3.1415;

2.双精度double 64bit

double db;
double db = 3.141592653589;

浮点型相关程序:

#include <stdio.h>

int main() {
    double a  = 3.14, b = 0.12;
    double c = a + b;
    printf("%f", c);
    return 0;
}

%f是float和double型的输出格式

对于浮点型来说,不要使用float,碰到浮点型的数据都应该使用double来存储

3.字符串常量

字符串是由若干字符组成的串,在C语言中没有单独一种基本数据类型可以存储(C++中有string类型),只能使用字符数组的方式。

#include <stdio.h>

int main() {
    char str1[25] = "Wo ai de ren bu ai wo";
    char str2[25] = "So sad a story it is.";
    printf("%s, %s", str1, str2);
    return 0; 
}

4.布尔型

bool变量:true,false

#include <stdio.h>

int main() {
    char str1[25] = "Wo ai de ren bu ai wo";
    char str2[25] = "So sad a story it is.";
    printf("%s, %s", str1, str2);
    return 0; 
}

2.1.3 强制类型转换

格式如下:

(新类型名) 变量名

案例说明:

#include <stdio.h>

int main() {
    double r = 12.56;
    int a = 3, b = 5;
    printf("%d\n", (int)r);
    printf("%d\n", a / b);
    printf("%.1f", (double)a / (double)b);
    return 0;
}

2.1.4 符号常量和const常量

符号常量即用一个标识符来替代常量,又称为“宏定义”或者“宏替换”,其格式如下:

#define 标识符 常量
#define pi 3.14
const 数据类型 变量名 = 常量;
const double pi = 3.14;
#define 标识符 任何语句或片段
#define ADD(a, b) (a) + (b)
可以直接使用ADD(a, b)来替代a+b 的功能

2.1.5 运算符

运算符就是用来计算的符号。常用的运算符有算术运算符、关系运算符、逻辑运算符、条件运算符、位运算符等。

1.算术运算符
算术运算符有很多,比较常用的是下面几个:

+加法运算符:将前后两个数相加。

–减法运算符:将前后两个数相减。

*乘法运算符:将前后两个数相乘。

/除法运算符:取前面的数除以后面的数得到的商。

%取模运算符:取前面的数除以后面的数得到的余数。

++自增运算符:令一个整型变量增加1.

--自减运算符:令一个整型变量减少1。

2.关系运算符

常用的关系运算符共有六种:<、>、<=、>=、--、!=

3.逻辑运算符

常用的逻辑运算符有三种:&&、l|、!,分别对应“与”“或”“非”

image-20210819110201071

4.条件运算符

A ? B : C

2.2 顺序结构

2.2.1 赋值表达式

int n = 5;
n = 6;

2.2.2 使用scanf和printf输入/输出

1.scanf函数的使用

scanf("格式控制", 变量地址);
scanf("%d", &n);
image-20210819141017616

2.printf函数的使用

printf("格式控制", 变量名称);
int n = 5;
printf("%d", n);
image-20210819141236949

2.2.3 使用getchar和putchar输入/输出字符

getchar用来输入单个字符,putchar用来输出单个字符,在某些scanf函数使用不便的场合可以使用getchar来输入字符。

#include <stdio.h>

int main() {
    char c1, c2, c3;
    c1 = getchar();
    getchar();
    c2 = getchar();
    c3 = getchar();
    putchar(c1);
    putchar(c2);
    putchar(c3);
    return 0;
}

2.2.4 注释

(1)多行注释:"/**/"

(2) 单行注释:”//“

2.2.5 typedef

typedef能给复杂的数据类型起别名,在使用中可以用别名来替代原来的写法。

2.2.6 常用math函数

1.fabs(double x)

该函数用于对double型变量取绝对值

2.floor(double x)和cell(double x)

这两个函数分别用于double型变量的向下取整和向上取整,返回类型为double型

3.pow(double r, double p)

该函数用于返回 r 的 p次方,要求 r 和 p都是double型

4.sqrt(double x)

该函数用于返回doublc型变量的算术平方根

5.log(double x)

该函数用于返回double型变量的以自然对数为底的对数

#include <stdio.h>
#include <math.h>
int main() {
    double db = log(1.0);
    printf("%f", db);
    return 0;
}

6.sin(double x)、cos(double x)和tan(doubel x)

这三个函数分别返回double型变量的正弦值、余弦值和正切值,参数要求是弧度制

7.asin(double x)、acos(double x)和atan(double x)

这三个函数分别返回double型变量的反正弦值、反余弦值和反正切值

8.round(double x)

该函数用于将double型变量x四舍五入,返回类型也是double型,需进行取整

2.3 选择结构

2.3.1 if语句

if(条件A){
    ....
}

2.3.2 if语句的嵌套

if(条件A){
    ...
    if(条件B){
        ...
    }else{
     	...
    }
	...
}

2.3.3 switch语句

switch(表达式){
        case 常量表达式1:
            ...
            break;
        case 常量表达式2:
            ...
            break;
    	default:
        	...
}

2.4 循环结构

2.4.1 while循环

while(条件A){
    ....
}

2.4.2 do···while语句

do{
    ...
}while(条件A);

2.4.3 for语句

for(表达式A; 表达式B; 表达式C){
    .....
}

①在for循环开始前,首先执行表达式A。
②判断表达式B是否成立:若成立,执行省略号内容;否则,退出循环。

③在省略号内容执行完毕后,执行表达式c,之后回到②。

为了理解上面的格式,下面举一个较为常用的特例:

for(循环变量赋初值; 循环条件; 循环变量改变){
    .......
}

2.4.4 break和continue语句

break:退出当前循环,执行剩下的语句

continue:退出本次循环,然后进去下一个轮回

2.5 数组

2.5.1 一维数组

数组就是把相同数据类型的变量组合在一起而产生的数据集合。

数据类型数组名[数组大小];注意:

数组大小必须是整数常量,不可以是变量。

int a[10];
double str[233];

数组访问:

数组名称[下标]

2.5.2 冒泡排序

排序是指将一个无序序列按某个规则进行有序排列,而冒泡排序是排序算法中最基础的一种。现给出一个序列a,其中元素的个数为n,要求将它们按从小到大的顺序排序。冒泡排序的本质在于交换,即每次通过交换的方式把当前剩余元素的最大值移动到一端,而当剩余元素减少为0时,排序结束。

#include <stdio.h>
int main() {
    int a[10] = {3, 1, 4, 5, 2};
    for(int i = 1; i <= 4; i++) { //进行 n - 1次
        //第i趟时从a[0]到a[n - i -1]都与它们下一个数比较
        for(int j = 0; j < 5 - i; j++) {
            if(a[j] > a[j + 1]) {
                //如果左边的数更大,则交换a[j]和a[j + 1]
                int temp = a[j];
                a[j]  = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
    for(int i = 0; i < 5; i++) {
        printf("%d", a[i]);
    }
    return 0;
}

2.5.3 二位数组

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

int a[5][6];
double db[10][10];

访问:数组名[下标1] [下标2]

2.5.4 memset----对数组中每一个元素赋相同的值

memset 函数的格式为:

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

2.5.5 字符数组

1.字符数组的初始化

char str[15] ={'G','o','o','t','o', 'r','y','!'};

2.字符数组的输入输出

(1)scanf输入,printf输出

(2)getchar输入,putchar输出

(3)gets输入,puts输出

3.字符数组的存放方式
由于字符数组是由若干个 char 类型的元素组成,因此字符数组的每一位都是一个char字符。除此之外,在一维字符数组(或是二维字符数组的第二维)的末尾都有一个空字符\0。

2.5.6 string.h头文件

1.strlen()

strlen 函数可以得到字符数组中第一个\0前的字符的个数,其格式如下:

strlen(字符数组):

2.strcmp()

strcmp函数返回两个字符串大小的比较结果,比较原则是按字典序,其格式如下:

strcmp(字符数组1,字符数组2)

3.strcpy()

strcpy函数可以把一个字符串复制给另一个字符串,其格式如下:

strcpy(字符数组1,字符数组2)

注意:是把字符数组⒉复制给字符数组1,这里的“复制”包括了结束符\0。

4.strcat()

strcat()可以把一个字符串接到另一个字符串后面,其格式如下:

strcat(字符数组1,字符数组2)

注意:是把字符数组2接到字符数组1后面,示例如下:

2.5.7 sscanf与sprintf

sscanf与 sprintf是处理字符串问题的利器,读者很有必要学会它们( sscanf从单词上可以理解为string + scanf,sprintf则可以理解为string + printf,均在stdio.h头文件下)。

2.6 函数

2.6.1 函数的定义

基本语法格式:

返回类型 函数名称(参数类型 参数){
    函数主体
}

2.6.2 再谈main函数

主函数对一个程序来说只能有一个,并且无论主函数写在哪个位置,整个程序一定是从主函数的第一个语句开始执行,然后在需要调用其他函数时才去调用。

int main(){
    ...
    return 0;
}

2.6.3 以数组作为函数参数

函数的参数也可以是数组,且数组作为参数时,参数中数组的第一维不需要填写长度(如果是二维数组,那么第二维需要填写长度),实际调用时也只需要填写数组名。最重要的是,数组作为参数时,在函数中对数组元素的修改就等同于是对原数组元素的修改(这与普通的局部变量不同)

2.6.4 函数的嵌套调用

函数的嵌套调用是指在一个函数中调用另一个函数,调用方式和之前main函数调用其他函数是一样的。

2.6.5 函数的递归调用

函数的递归调用是指一个函数调用该函数自身。

2.7 指针

2.8 结构体(struct)的使用

2.8.1 结构体的定义

定义一个结构体的基本格式如下:

struct Name{
    //一些基本的数据结构或者自定义的数据类型
};

# 初始化
struct Name{
    //一些基本的数据结构或者自定义的数据类型
}Name1, Name2;

需要注意的是,结构体里面能定义除了自己本身(这样会引起循环定义的问题)之外的任何数据类型。不过虽然不能定义自已本身,但可以定义自身类型的指针变量。例如:

struct node {
node n; //不能定义node型变量
node next;//可以定义node*型指针变量
};

2.8.2 访问结构体内的元素

访问结构体内的元素有两种方法:“.”操作和“->”操作。

2.8.3 结构体的初始化

1.定义时初始化

定义一个studentInfostu的结构体变量,然后对其中的元素逐一赋值,以达到初始化的目的,示例如下:

stu.id =1;
stu.gender ='M”;

或者在读入时进行赋值;

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

2.构造函数

所谓构造函数就是用来初始化结构体的一种函数,它直接定义在结构体中。构造函数的一个特点是它不需要写返回类型且函数名与结构体名相同

struct studentInfo{
	int id;
	char gender;
	//默认生成的构造函数
    studentInfo(){};
};

构造函数简化成一行:

studentInfo(int _id, char _gender): id(_id), gender(_gender){}
# 赋值
studentInfostu = studentInfo(10086,"M');

注意:如果自己重新定义了构造函数,则不能不经初始化就定义结构体变量,也就是说,默认生成的构造函数“studentInfo(){}”此时被覆盖了。为了既能不初始化就定义结构体变量,又能享受初始化带来的便捷,可以把“studentInfo{}”手动加上。

2.9 补充

2.9.1 cin与cout

1.cin

cin是c和 in的合成词,采用输入运算符“>>”来进行输入。可以发现,cin的输入不指定格式,也不需要加取地址运算符&,直接写变量名就可以了。如果同时读入多个变量也是一样的写法,只需要往后面使用>>进行扩展即可。

int n;
cin >> n;

2.cout

cout是c和 out的合成词,其使用方法和 cin几乎是一致的,只不过使用的是输出运算符<<。

对cout来说,换行有两种方式:第一种和C中相同,也就是使用n来进行换行;第二种方法则是使用endl 来表示换行(endl是 end line的缩写):

2.9.2 浮点数的比较

1.等于运算符(==)

2.大于运算符(>)

3.小于运算符(<)

4.大于等于运算符(>=)

5.小于等于运算符(<=)

6.圆周率Π

2.9.3 复杂度

1.时间复杂度

简单地说,时间复杂度是算法需要执行基本运算的次数所处的等级,其中基本运算就是类似加减乘除这种计算机可以直接实现的运算。时间复杂度是评判算法时间效率的有效标准。在时间复杂度中,高等级的幂次会覆盖低等级的幂次。

2.空间复杂度

和时间复杂度类似,空间复杂度采用相同的写法,表示算法需要消耗的最大数据空间。

3.编码复杂度

编码复杂度是一个定性的概念,并没有什么量化的标准。对一个问题来说,如果使用了冗长的算法思想,那么代码量将会非常巨大,其编码复杂度就会非常大。

2.10 黑盒测试

2.10.1 单点测试

对单点测试来说,系统会判断每组数据的输出结果是否正确。如果输出正确,那么对该组数据来说就通过了测试,并获得了这组数据的分值。在这种情况下,题目的总得分等于通过的数据的分值之和。PAT 就是采用了单点测试,并且对每组数据都会给出相应的测评结果。

2.10.2 多点测试

与单点测试相对,多点测试要求程序能一次运行所有数据,并要求所有输出结果都必须完全正确,才能算作这题通过;而只要有其中一组数据的输出错误,本题就只能得0分。

(1)while...EOF型

C语言中使用EOF(即End Of File)来代表-1

while(scanf("%d", &n) != EOF){
    .....
}

(2)while....break型

这种类型是while.… EOF 型的延伸,题目要求当输入的数据满足某个条件时停止输入。这种类型是while.… EOF 型的延伸,题目要求当输入的数据满足某个条件时停止输入。

(3)while(T--)型

在这种类型中,题目会给出测试数据的组数,然后才给出相应数量组数的输入数据。

#include <stdio.h>

int main(){
    int T, a, b;
    scanf("%d", &T);
    while(T--){

        scanf("%d%d", &a, &b);
        printf("%d\n", a + b);
    }
    return 0;
}

以上就是多点测试的三种输入类型。下面讲解三种常见的输出类型。

(1)正常输出

这种输出类型要求需要每两组输出数据中间没有额外的空行,即输出数据是连续的多行。

(2)每组数据输出之后都额外加一个空行

这个要求非常容易实现,只需要在每组输出结束之后额外输出一个换行符n即可。

(3)两组输出数据之间有一个空行,最后一组数据后面没有空行

这一般是在第三种输入类型 while(T--)的情况下,只需要通过判断T是否已经减小到0来判断是否应当输出额外的换行。

最后需要指出,在多点测试中,每一次循环都要重置一下变量和数组,否则在下一组数据来临的时候变量和数组的状态就不是初始状态了,而重置数组一般使用memset函数或f函数。

posted @ 2021-08-19 21:41  NotYourferry  阅读(311)  评论(0编辑  收藏  举报