作弊是否很有诱惑性 ?
• 上堂课已经讲了– 作弊是有记录的– 心理系很多同学集体作弊,让人震惊– 下次再让抓到作弊,就不客气了
• 作业不一定在两个小时里都能完成
• 答疑没有一个人?
C 语言高级编程(第一部分) 数组与算法 I
北京大学 信息科学技术学院
关于 宏
形式:#define PI 3.14159
宏名(一般用大写,以区别一般变量)
宏体(可以是一个复杂
的表达式)
作用与意义 1.在源程序中,用简单的宏名 指代 复杂的宏体 2.程序更加直观
形式:#define PI 3.14159
注意: 1.#define 可出现在程序的任一行 2.宏的作用范围:由 宏定义开始 到 程序末尾 之间的区域 3.宏定义不是 C 语句,不必在行尾加分号
#define PI 3.141593; #include <stdio.h> int main( ) {
double S; S = PI - PI; printf(“%lf\n”, S);
return 0; }
S 的值是多少?
按下 build 后 … …源文件
可执行程序
预编译 编译 连接
没有宏的源文件
二进制代码
#define PI 3.141593;
int main( ) {
double S;
S = PI - PI;
return 0;
}
预编译
int main( ) {
double S;
S = 3.141593; - 3.141593;;
return 0;
}
把源程序中的所有 PI 替换为 3.141593;
源文件没有宏的
源文件
int main( ) {
double S;
S = 3.141593; - 3.141593;;
return 0;
}int main( ) {
double S;
S = 3.141593;
- 3.141593;
;
return 0;
}
错误 :
使用 宏 定义常量时,在结尾加 ;
#define PI 3.14159;int main( ) {
return 0;
}
#define PI 3.14159 int main( ) {
return 0;
}✕ ✓
宏定义其他例子:
// 整数常量#define MAX 200
几个 例子
#include <stdio.h>int main(){
int a, b;int e;
scanf("%d", &a);scanf("%d", &b);
………………
printf("%d %d\n", a, b);
return 0;}
程序填空,要求:输出的两个数值按照从小到大 的顺序排列
#include <stdio.h>int main(){
int a, b;int e;
scanf("%d", &a);scanf("%d", &b);
if(a > b){e = a;a = b;b = e;
}
printf("%d %d\n", a, b); return 0;
}
#include <stdio.h>int main(){
int a, b, c;int e;
scanf("%d", &a);scanf("%d", &b);scanf("%d", &c);
………………
printf("%d %d\n", a, b, c);
return 0;}
程序填空,要求:输出的 3 个数值按照从小到大 的顺序排列
#include <stdio.h>int main(){
int a, b, c;int e;scanf("%d", &a);scanf("%d", &b);scanf("%d", &c);if(a > b){
e = a;a = b;b = e;
}if(b > c){
e = b;b = c;c = e;
}
if(a > b){e = a;a = b;b = e;
}
printf("%d %d %d\n", a, b, c);
return 0;}
如果要求:1. 接收用户输入的 10 个数值2. 输出的 10 个数值按照 从小到大 的顺序排列
难道需要这样定义 10 个变量吗?int a, b, c, d, e, f, g, h, i, j;
如果要输入更多的数值呢?!有没有一种 更简便的方式,可以一次定义一组变量?
数组
数组 是 什么?数组 是 一组变量
数组 是 一组 具有编号 的、类型相同 的变量
数组 是 一组 类型相同 的变量
如何声明 一个数组
int sz[10];
数组的类型 数组名 数组中变量的数目
必须是一个常量
数组 中 变量 的 编号
int sz[10];
数组中变量的编号
从 0 开始; 到 数组的长度- 1 结束
0 1 2 3 4 5 6 7 8 9
如何访问 数组 中 的 变量int sz[10];
sz[0] = 1;sz[1] = 3;sz[2] = 7;…sz[8] = 16;sz[9] = 12;
…sz[2]…;
数组名
变量编号
数组变量赋值 的 一种特殊方式
• 声明时赋值
int sz[5] = { 12, 3, 7, 28, -2 } ;
访问 数组中 变量的一种常见方式
int sz[10];int i;…i =…;sz[i] = ……
i 的值可以根据需要变化
#include <stdio.h>int main(){
int sz[10];int i;
for( ; ; ){ ;
}
for(i = 0; i < 10; i++){printf("%d ", sz[i]);
}
return 0;}
程序填空,要求:1. 接收用户输入的 10 个数字2. 存放在数组 sz 中
#include <stdio.h>int main(){
int sz[10];int i;
for( i=0 ; i < 10 ; i++){
scanf("%d", &(sz[i]));
}
for(i = 0; i < 10; i++){printf("%d ", sz[i]);
}
return 0;}
数组 的 遍历通过 循环结构
正向 遍历#define LEN 10int sz[LEN];
for(int i = 0; i < LEN; i++){… sz[i] …
}
反向 遍历
for(int i = LEN-1; i >= 0; i--){… sz[i] …
}
#define LEN 10int sz[LEN];
通过遍历 实现 对 数组变量 的 控制台赋值
for(int i = 0; i < LEN; i++){scanf(“%d”, &(sz[i]));
}
#define LEN 10int sz[LEN];
通过遍历 实现 对 数组变量 的 控制台输出
for(int i = 0; i < LEN; i++){printf(“%d\n”, sz[i]);
}
#define LEN 10int sz[LEN];
数组 的 应用示例当程序要处理
一组类型相同、含义类似的数据时,应该使用数组
游戏:过年抽奖• 一个村庄,有 128 个村民• 村长对村民说:– 今年村里出现了财政盈余 M 元;– M 是 2000 - 3000 之间的一个整数– 准备通过抽奖的方式把钱发给村民
• 游戏规则如下:– 每个村民上报一个在 2000 - 3000 元之间的整数– 如果有人上报的数字和 M 相等,就把钱发给这些人
• 如果只有一个村民猜对,就把 M 元钱全部发给他• 如果有多个人村民猜对,就把 M 元钱平均分配给这些村民• 如果没有人猜对,财政盈余转为下年度开支
村长邀请我们编写一个程序, 实现村里财政盈余的自动分配
编程的基本思路• 定义一个数组 存放所有村民上报的数据• 定义一个数组 存放获奖者的编号(幸运者数组)• 定义一个整数 存放获奖者人数• 对村民从 0开始编号(最后一个编号是 127)• 村民按照编号顺序上报数字;程序将村民上报的数字
存放在对应编号的数组变量中。• 遍历村民上报的数字,若上报数字与幸运数相等,则
– 将村民编号 按顺序 添加到幸运者数组中;– 并将获奖者人数加 1
• 最后,打印出获奖者编号和获得的奖金数额
#define LUCKY_M 2345 // 财政盈余#define POPULATION 128 // 村民数量int main( ){ int people[POPULATION]; // 记录所有村民上报数字的数组 int luckyPeople[POPULATION]; // 幸运者数组,记录获奖者编号,为什么和村民一样多? int i, nLucky=0; // 循环变量,获奖者人数 for (i=0; i<POPULATION; i++) { scanf(“%d”, &(people[i])); // 读入村民报的数字,数组下标就是村民的编号 } for (i=0; i<POPULATION; i++) { if ( people[i] == LUCKY_M ) { luckyPeople[nLucky] = i; nLucky ++; } } if (nLucky > 0){ for (i=0; i<nLucky; i++){ // 输出获奖者编号及所获奖金数额 printf("%d %d\n", luckyPeople[i], LUCKY_M / nLucky); } return 0; } return -1;}
利用 数组, 对一组数据 进行排序显然,不能采用简单的比较。需要有更有效的方法。
1. 冒泡排序法2. 选择排序法
排序的基本方法如何把大象关在冰箱里 ?
• 分 3步:– 第一步:打开冰箱门 ;
– 第二步:把大象推进冰箱 ;
– 第三步:关上冰箱门 ;
• 分 8 个步骤进行– 第 1步:把第 1 大的数放在变量 sz[7] 中;
– 第 2步:把第 2 大的数放在变量 sz[6] 中;
– 第 3步:把第 3 大的数放在变量 sz[5] 中;
– 第 4步:把第 4大的数放在变量 sz[4] 中;
– 第 5步:把第 5大的数放在变量 sz[3] 中;
– 第 6步:把第 6大的数放在变量 sz[2] 中;
– 第 7步:把第 7大的数放在变量 sz[1] 中;
– 第 8步:把第 8 大的数放在变量 sz[0] 中;
排序的基本方法int sz[LEN]; // # define LEN 8
编号 排序前的值
排序后的值
0 16 11
1 15 12
2 14 13
3 18 14
4 17 15
5 13 16
6 12 17
7 11 18
排序的基本方法
• 分 8 个步骤进行
– 第 1步:把第 1 大的数放在变量 sz[7] 中;
– 第 2步:把第 2 大的数放在变量 sz[6] 中;
– 第 3步:把第 3 大的数放在变量 sz[5] 中;
– 第 4步:把第 4大的数放在变量 sz[4] 中;
– 第 5步:把第 5大的数放在变量 sz[3] 中;
– 第 6步:把第 6大的数放在变量 sz[2] 中;
– 第 7步:把第 7大的数放在变量 sz[1] 中;
– 第 8步:把第 8 大的数放在变量 sz[0] 中;
int sz[LEN]; // # define LEN 8
• LEN 个数,分 LEN 个步骤进行
– 第 1步:把第 1 大的数放在变量 sz[LEN-1] 中;
– 第 2步:把第 2 大的数放在变量 sz[LEN-2] 中;
– 第 3步:把第 3 大的数放在变量 sz[LEN-3] 中;
– 第 4 步:把第 4大的数放在变量 sz[LEN-4] 中;
– 第 5 步:把第 5大的数放在变量 sz[LEN-5] 中;
– 第 6 步:把第 6大的数放在变量 sz[LEN-6] 中;
– …
– 第 LEN步:把第 LEN 大的数放在变量 sz[LEN-LEN] 中;
抽象
排序的基本方法• LEN 个数,分 LEN 个步骤进行– 第 1步:把第 1 大的数放在变量 sz[LEN-1] 中;
– 第 2步:把第 2 大的数放在变量 sz[LEN-2] 中;
– 第 3步:把第 3 大的数放在变量 sz[LEN-3] 中;
– 第 4 步:把第 4大的数放在变量 sz[LEN-4] 中;
– 第 5 步:把第 5大的数放在变量 sz[LEN-5] 中;
– 第 6 步:把第 6大的数放在变量 sz[LEN-6] 中;
– …
– 第 LEN步:把第 LEN 大的数放在变量 sz[LEN-LEN] 中;
排序的基本方法
对数组 int sz[LEN]进行排序,可以分为 LEN 个步骤进行。
第 k 步:把第 k 大的数放在变量 sz[LEN-k] 中;
( K=1, 2, 3, 4, …, LEN-1, LEN )
int e;for(int i = 0; i < LEN - 1; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 作第 1步:把第 1 大的数放在变量 sz[LEN-1] 中;
int e;for(int i = 0; i < LEN - 2; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 作第 2 步:把第 2 大的数放在变量 sz[LEN-2] 中;
int e;for(int i = 0; i < LEN - 3; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 作第 3 步:把第 3 大的数放在变量 sz[LEN-3] 中;
int e;for(int i = 0; i < LEN - k; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 作第 k 步:把第 k大的数放在变量 sz[LEN-k] 中;
用 冒泡法 对 数组 int sz[LEN] 进行排序
int e;for(int i = 0; i < LEN - k; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 对 数组 int sz[LEN] 进行排序
int e;
for(int i = 0; i < LEN - k; i++){if(sz[i] > sz[i+1]){
e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 对 数组 int sz[LEN] 进行排序
int e;
for(int i = 0; i < LEN - k; i++){if(sz[i] > sz[i+1]){
e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
用 冒泡法 对 数组 int sz[LEN] 进行排序int e;
for(;;){for(int i = 0; i < LEN - k; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
}
用 冒泡法 对 数组 int sz[LEN] 进行排序int e;
for(int k = ; k <= ; k++){for(int i = 0; i < LEN - k; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
}
用 冒泡法 对 数组 int sz[LEN] 进行排序int e;
for(int k = 1 ; k <= ; k++){for(int i = 0; i < LEN - k; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
}
用 冒泡法 对 数组 int sz[LEN] 进行排序int e;
for(int k = 1 ; k <= LEN ; k++){for(int i = 0; i < LEN - k; i++){
if(sz[i] > sz[i+1]){e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
}
用 冒泡法 对 数组 int sz[LEN] 进行排序
int e;for(int k = 1 ; k <= LEN ; k++){
for(int i = 0; i < LEN - k; i++){if(sz[i] > sz[i+1]){
e = sz[i+1];sz[i+1] = sz[i];sz[i] = e;
}}
}
对该算法进行测试编写一个程序:1. 从控制台输入一组 数据,并将其存放 一个数组中;2. 用左侧算法进行排 序;3.打印出排序结果;
选择 排序法
排序的基本方法
对数组 int sz[LEN]进行排序,可以分为 LEN 个步骤进行。
第 k 步:把第 k 大的数放在变量 sz[LEN-k] 中;
( K=1, 2, 3, 4, …, LEN-1, LEN )
用 选择法 作 第 1步:把第 1 大的数放在变量
sz[LEN-1] 中;int maxIndex, e;maxIndex = 0;for(int i = 0; i <= LEN-1; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-1){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-1];sz[LEN-1] = e;
}
用 选择法 作 第 2 步:把第 2 大的数放在变量
sz[LEN-2] 中;int maxIndex, e;maxIndex = 0;for(int i = 0; i <= LEN-2; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-2){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-2];sz[LEN-2] = e;
}
用 选择法 作 第 3 步:把第 3 大的数放在变量
sz[LEN-3] 中;int maxIndex, e;maxIndex = 0;for(int i = 0; i <= LEN-3; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-3){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-3];sz[LEN-3] = e;
}
用 选择法 作 第 k 步:把第 k大的数放在变量
sz[LEN-k] 中;int maxIndex, e;maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}
用 选择法 对 数组 int sz[LEN] 进行排序;
int maxIndex, e;maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}
用 选择法 对 数组 int sz[LEN] 进行排序;int maxIndex, e;
maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}
用 选择法 对 数组 int sz[LEN] 进行排序;int maxIndex, e;
maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}
用 选择法 对 数组 int sz[LEN] 进行排序;int maxIndex, e;
for(;;){maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}}
用 选择法 对 数组 int sz[LEN] 进行排序;int maxIndex, e;
for(int k = ; k <= ; k++){maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}}
用 选择法 对 数组 int sz[LEN] 进行排序;int maxIndex, e;
for(int k = 1 ; k <= LEN; k++){maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}}
用 选择法 对 数组 int sz[LEN] 进行排序;int maxIndex, e;
for(int k = 1 ; k <= LEN; k++){maxIndex = 0;for(int i = 0; i <= LEN-k; i++){
if(sz[i] > sz[maxIndex]){maxIndex = i;
}}if(maxIndex != LEN-k){
e = sz[maxIndex];sz[maxIndex] = sz[LEN-k];sz[LEN-k] = e;
}}
对该算法进行测试编写一个程序:1. 从控制台输入一组 数据,并将其存放 一个数组中;2. 用左侧算法进行排 序;3.打印出排序结果;
一些问题的程序求解
问题 1 :判断某一年份是否是闰年
什么是闰年?
闰年的充分必要条件是:“年份能被 400 整除”
或者“年份能被 4整除,但不能被 100 整除”
int main(){int year;scanf(“%d”, &year);if(year%400 == 0 ||(year%4==0 && year%100!=0)){
printf(“%d is RuiNian.”, year);} else{
printf(“%d not RuiNian.”, year);}
return 0;}
判断闰年的函数
int isRunNian(int year){int result;if(year%400 == 0 ||(year%4==0 && year%100!=0)){
result = 1;} else{
result = 0;}
return result;}
问题 2 :给定一个年月日,
判断这一天是这一年的第几天
求解思路• step1: –将该日所在月份之间的所有月份的天数加起来;–并存入一个变量中;
• step2 :–将该日的日期号加入这个变量中;
• step3 :– 输出该变量;
int main(){int year, month, day, total;scanf(“%d %d %d”, &year, &month, &day);total = 0;for(int i = 1; i < month; i++){ //step1
if(i==1||i==3||i==5||i==7||i==8||i==10||i==12){total += 31;
} else if (i == 4 || i ==6 || i == 9 || i==11){total += 30;
} else if(i == 2){if(isRunNian(year)){
total += 29;} else {
total += 28;}
}}total += day; //step2printf(“%d\n”, total); //step3
return 0;}
求解某一日期是当年的第几天的函数int DiJiTian(int year, int month, int day){
int result = 0;for(int i = 1; i < month; i++){ //step1
if(i==1||i==3||i==5||i==7||i==8||i==10||i==12){result += 31;
} else if (i == 4 || i ==6 || i == 9 || i==11){result += 30;
} else if(i == 2){if(isRunNian(year)){
result += 29;} else {
result += 28;}
}}result += day; //step2return result;
}
问题 3 :计算细菌数量
1. 假设细菌的数量每天成倍增长2. 给定一个 开始日期 和 细菌数量3. 给定一个 终止日期 求终止日期时的细菌数量
求解思路• step1:–计算 开始日期 和 终止日期 之间的天数;
• step2:–计算细菌数量;
• step3:–打印出细菌数量;
int main(){int year, m1, d1, num, m2, d2;scanf(“%d %d %d %d %d %d”, &year, &m1, &d1, &num, &m2, &d2);
int days = DiJiTian(year, m2, d2) - DiJiTian(year, m1, d1);int result = num;
for(int i = 0; i <= days; i++){result *= 2;
}
printf(“%d\n”, result);
return 0;}
课堂作业
编写程序,实现下述功能:1, 从控制台输入 两个 年 月 日2 ,调用 DiJiTian函数计算这两个日期之间 相隔几天3 ,向控制台输出结果
请同学们拿出笔和纸,1. 写下 姓名和学号;2. 写下 满足上述要求的程序;