500 likes | 648 Views
第十讲:复合数据结构 —— 数组与结构. 北京大学 信息科学技术学院 2013 年 1 2 月. #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<br>", a, b); return 0; }. #include < stdio.h > int main(){ int a, b, c; int e;
E N D
第十讲:复合数据结构——数组与结构 北京大学信息科学技术学院 2013年12月
#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); 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 2 3 4 5 6 7 8 9 数组中变量的编号 从0开始; 到数组的长度-1结束
如何访问数组中的变量 变量编号 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};
#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(){ intsz[10]; inti; for( i=0 ; i < 10 ;i++){ scanf("%d", &(sz[i])); } for(i = 0; i < 10; i++){ printf("%d ", sz[i]); } return 0; }
数组的遍历 通过循环结构
正向遍历 int sz[LEN]; for(int i = 0; i < LEN; i++){ … sz[i] … }
反向遍历 int sz[LEN]; for(int i = LEN-1; i>= 0; i--){ … sz[i] … }
通过遍历实现对数组变量的控制台赋值 int sz[LEN]; for(inti= 0; i < LEN; i++){ scanf(“%d”, &(sz[i])); }
通过遍历实现对数组变量的控制台输出 int sz[LEN]; for(int i = 0; i < LEN; i++){ printf(“%d\n”, sz[i]); }
数组的应用示例 当程序要处理 一组类型相同、含义类似的数据时, 应该使用数组
游戏:过年抽奖 • 一个村庄,有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 nLucky=0; //获奖者人数 int i; //循环变量 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 ++; } } //输出获奖者编号及所获奖金数额 for (i=0; i<nLucky; i++){ printf("%d %d\n", luckyPeople[i], LUCKY_M / nLucky); } return 0; }
回顾排序:冒泡排序 #defineLEN10 • inti,k,e,sz[LEN]; • for(i = 0; i < LEN; i++){ • scanf(“%d”, &(sz[i])); • } • for(k = 1 ; k <= LEN ; k++){ • for(i= 0; i < LEN - k; i++){ • if(sz[i] >sz[i+1]){ • e = sz[i+1]; • sz[i+1] = sz[i]; • sz[i] = e; • } • } • }
结构的概念 • 通常,一个学生的个人信息,包括:学号、姓名、性别、年龄、各门功课的成绩等数据,这些数据都与一个学生相关联,类型各不相同。如果将这些数据定义为各独立的简单变量: • Number、Name、Sex、Age、Course1、Course2、… • 这样就难以反映它们之间的内在联系。应该把它们组织成一个组合项,把它们当作一个有机的整体。 • ——这个组合项就是结构(Structure)
结构类型及其定义 • 把多个紧密关联的变量(分量)顺序组织在一起,定义成一个新的复合数据类型——结构类型 • 定义一个结构类型 struct结构类型名 { 类型1 分量名1; 类型2 分量名2; ...... }; • 结构分量的类型可以相同,也可不同 • 同一个结构内的分量名不可相同 structpoint { float x; float y; };
结构类型变量的定义 • 结构类型只是定义了一种新的数据类型 • 系统并不为这个新类型分配内存空间。 • 可以使用新的结构类型来声明变量——结构类型变量。 • 结构类型变量定义的两种形式: • 用已定义的结构定义变量,例如: struct point point1; struct point point2; • 定义结构的同时定义结构类型的变量,例如: struct city{ float x, y; int population; } city1, city2; • 系统会为结构类型变量分配内存空间
结构类型变量中分量的访问 • 结构类型变量的值由其各个分量构成 • 对分量的访问一般通过“变量名.分量名”完成 • 结构赋值及访问的例子: float dx, dy; struct point { float x, y; } p1, p2, points[2]; p1.x = p1.y = 3.5f; p2.x = p2.y = 1.5f; dx = p1.x - p2.x; dy = p1.y - p2.y; • 结构变量本身可以作为一个整体来使用 points[0] = p1; points[1] = p2;
struct city { char name[32]; struct city city1; }x; 结构类型中的分量 • 结构类型中分量的类型可以是任何类型 • 基本数据类型的分量 struct point{ float x, y; }; • 其他类型的分量:结构类型、数组类型 • 分量的类型不能是未定义的结构类型 • 分量的类型不能是正在定义的结构类型 • struct city { • struct point{ • float x, y; • }location; • int population; • char name[32]; • }city1; • struct city{ • struct point location; • int population; • char name[32]; • }city1; (city1.location).x
主存储器 10 20 100 200 * * 结构变量的内存布局 • 结构中各分量在内存中顺序存放 struct square { struct point { int x, y; } p1, p2; } sq1; sq1.p1.x = 10; sq1.p1.y = 20; sq1.p2.x = 100; sq1.p2.y = 200; sq1.p1.x sq1.p1.y sq1.p2.x sq1.p2.y
结构变量所占内存的大小 • 结构变量所占内存的大小并不完全等于于各分量所占字节数的总和 structchar_frequency { char c; int frequency; }; sizeof(strcutchar_frequency)通常为8,而非5 这是编译器在编译时的一个特殊要求。
结构应用示例(1)救援 • 洪水淹没了很多房子,只有屋顶还是安全的。被困的人们都爬上了屋顶。现在救生船每次都从大本营出发,到各屋顶救人,救了人之后将人送回大本营。 • 救生船每次从大本营出发,以速度50米/分钟时向下一个屋顶,达到一个屋顶后,救下其上的所有人,每人上船1分钟,船原路返回,达到大本营,每人下船0.5分钟。 • 假设大本营与任意一个屋顶的连线不穿过其它屋顶。 • 输入:第一行是屋顶数n,其后n行,每行是每个屋顶的坐标和人数 • 输出:第一行是所有人都到达大本营并登陆所用的时间,其后n行,每行是每个屋顶的坐标和人数
图中原点是大本营,每个点代表屋顶, 每个屋顶由其位置坐标和其上的人数表示。
#include <stdio.h> #include <math.h> #include <malloc.h> #define MAX_ROOF_NUM 1000 #define SPEED 50.0 #define UP 1.0 #define DOWN 0.5 intmain() { introof_num; // 屋顶数. scanf("%d", &roof_num); // 输入屋顶数. struct roof { float x, y; // 屋顶坐标. intp; // 屋顶上的人数. } rfs[MAX_ROOF_NUM]; doubletotalTime = 0; // 救援总时间. inti; // 循环控制变量. //用循环处理每一个屋顶,输入屋顶位置及人数数. for (i=0; i<roof_num; i++){
//用循环处理每一个屋顶,输入屋顶位置及人数.//用循环处理每一个屋顶,输入屋顶位置及人数. for (i=0; i<roof_num; i++{{ scanf("%f%f%d",&(rfs[i].x), &(rfs[i].y), &(rfs[i].p)); } // 用循环处理每一个屋顶,计算救援时间 . for (i=0; i<roof_num; i++) { // 首先计算从大本营到屋顶的双程航行时间,并累加到总救援时间中 . totalTime += 2 * sqrt(rfs[i].x*rfs[i].x + rfs[i].y*rfs[i].y) / SPEED; // 然后计算被求人员上船和下船所耗费的总时间,并累加到总救援时间中 . totalTime += rfs[i].p * (UP + DOWN); } // 打印出救援总时间. printf("Total Time is: %.2lf\n", totalTime); // 依次打印出各屋顶的位置及人数. for (i=0; i<roof_num; i++) { printf("Roof ID: %d %.2f %.2f %d\n", i+1, rfs[i].x, rfs[i].y, rfs[i].p); } return 0; }
结构应用示例(2)学生成绩统计 • 定义一个结构,包含学生的所有信息。 struct student { int number; char name[8]; char sex; int age; float course[8]; }; struct student class1[160];
单个变量、数组和结构 • 数组和结构:多个变量的集合 • 数组 • 通过数组可定义大量类型相同的变量 • 数组元素通过“变量[下标]”形式访问 • 静态数组的大小(数组元素的个数)是预先确定的,即数组定义中数组个数必须是整数常量 • 结构 • 结构把一组密切相关的变量(类型可以不同)组织成一个整体 • 结构的分量通过"变量. 分量"形式访问
单个变量、数组和结构 • 更加复杂的数据定义 • 结构中的数组 • 结构中的结构 • 结构数组
复杂结构示例:教师、学生、班级 structteacher_s { // 教师结构 char name[32]; // 姓名 char title[16]; // 职称 intage; // 年龄 }; structstudent_s { // 学生结构 char name[32]; // 姓名 char sex; // 性别 intage; // 年龄 }; structclass_s{ // 班级结构 structteacher_steacher; // 教师 structstudent_s students[180]; // 所有学生 intstudent_num; // 学生人数 };
练习5:求两个正整数的最大公约数 • 接收从控制台输出的两个整数 • (假设是两个正整数) • 输出这两个数的最大公约数
#include <stdio.h> intmain() { inta, b, t; scanf("%d%d", &a, &b); // a和b的最大共数也是b和a%b的最大公约数 t = a % b; while (t != 0) { a = b; b = t; t = a % b; } printf("%d\n", b); return 0; }
练习6:逆序排列数组中的元素 • 接收从控制台输出的一组整数(假设输入n个整数,n <= 100) • 按输入相反的顺序输出这组整数
#include <stdio.h> #define MAX 100 intmain() { inta[MAX], i, j, n, t; scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", &a[i]); } for (i = 0, j = n-1; i < j; i++, j--) { t = a[i]; a[i] = a[j]; a[j] = t; } for (i = 0; i < n; i++) { printf("%d\n", a[i]); } return 0; }
#include <stdio.h> #define MAX 100 intmain() { inta[MAX], i, j, n, t; scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", &a[i]); } for (i = 0, j = n-1; i < j; i++, j--) { t = a[i]; a[i] = a[j]; a[j] = t; } for (i = 0; i < n; i++) { printf("%d\n", a[i]); } return 0; }
练习7:循环右移数组中的元素 • 接收从控制台输出的一组整数(假设输入n个整数,n <= 100) • 将数组中后m个数移动到数组头部 • 要求不能使用辅助数组
#include <stdio.h> #define MAX 100 intmain() { inta[MAX], i, j, m, n, t; scanf("%d%d", &n, &m); for (i = 0; i < n; i++) { scanf("%d", &a[i]); } for (j = 0; j < m; j++) { t = a[n-1]; for (i = n-1; i > 0; i--) { a[i] = a[i-1]; } a[0] = t; } for (i = 0; i < n; i++) { printf("%d\n", a[i]); } return 0; }
#include <stdio.h> #define MAX 100 intmain() { inta[MAX], i, j, m, n, t; scanf("%d%d", &n, &m); for (i = 0; i < n; i++) { scanf("%d", &a[i]); } for (j = 0; j < m; j++) { t = a[n-1]; // 保存最后一个元素 for (i = n-1; i > 0; i--) { a[i] = a[i-1]; // 每个元素向右移动一步 } a[0] = t; // 将保存的最后一个元素移动到首位置 } for (i = 0; i < n; i++) { printf("%d\n", a[i]); } return 0; }
…// 省略 inta[MAX], i, j, m, n, t, o, p, x; …// 省略 o = m; p = n; t = o % p; while (t != 0) { o = p; p = t; t = o % p; } // 求 p 为 n 和 m 的最大公约数 o = n / p; for (j = i = 0; j < p; j++) { t = a[i]; // t 为要右移的元素值 for (k = 1; k < o; k++) { i = (i + m) % n; // 计算移动的目标位置 x = t; // t 与 a[i] 交换 t = a[i]; a[i] = x; } a[j] = t; // 最后的 t 移到a[j]处 } …// 省略
课堂作业:选择性输出 • 问题描述 • 分别按顺序输出输入的自然数和负整数。 • 程序输入 • 第一个整数为输入整数数量n(n<100) • 紧跟着n个整数(可能是正整数,零或负整数) • 程序输出 • 先按输入顺序输出所有的自然数; • 再按输入顺序输出所有的负整数; • (注:每个整数占一行)。 课堂 作业 在纸上写出这段程序,标上姓名、学号; 写完后,交给我,即可离开。