1.09k likes | 1.26k Views
5 数据组织、筛法与排序问题 的解题思路. 教学内容: 1 数组的概念 2 筛法求素数 3 冒泡排序. 教学方法:任务驱动. 课时安排: 2节课. 学 习 目 标. 数组的概念、定义和初始化 筛法的解题思路 冒泡排序的思路. 内 容 要 点. 数组:定义、初始化、操作与应用 筛法:do_while循环、 while循环、求质数 排序:冒泡排序的算法. 问题:哪只羊最重?.
E N D
5数据组织、筛法与排序问题的解题思路 • 教学内容: • 1 数组的概念 • 2 筛法求素数 • 3 冒泡排序 教学方法:任务驱动 课时安排:2节课
学 习 目 标 • 数组的概念、定义和初始化 • 筛法的解题思路 • 冒泡排序的思路
内 容 要 点 • 数组:定义、初始化、操作与应用 • 筛法:do_while循环、 while循环、求质数 • 排序:冒泡排序的算法
问题:哪只羊最重? 中秋佳节,有贵客来到草原,主人要从羊群中选一只肥羊宴请宾客,当然要选最重者。这样就要记录每只羊的重量,如果有成千上万只羊,不可能用一般变量来记录。可以用带有下标的变量,也就是这里要讲的数组。 我们先看例子:用键盘输入10只羊的重量存放到一个名为sheep的数组中
//************************************ //* 程序名:5_1.cpp(数组示例) * //* 作 者:wuwh * //* 编制时间:2002年9月20日 * //* 主要功能:找出最重的羊 * //************************************ #include <iostream> // 预编译命令 #include <memory> // 预编译命令 using namespace std; int main() // 主函数 { float sheep[10]; // 数组,有10个浮点类型元素, // 用于存10只羊每一只的重量 memset ( sheep, 0 , sizeof (sheep) );// 初始化数组元素为0 float bigsheep=0.0; // 浮点类型变量,存放最肥羊的重量 int i=0, bigsheepNo=0; // 整型变量,i 用于计数循环, // bigsheepNo用于记录最肥羊的号
for ( i=0; i<10; i=i+1 ) // 计数循环 { // 循环,开始 cout<<"请输入羊的重量sheep["<< i << "]="; // 提示用 cin >> sheep[i]; // 输入第i只羊的重量 if ( bigsheep < sheep[i] ) // 如果第i只羊比当前最肥羊大 { bigsheep = sheep[i]; // 让第i只羊为当前最肥羊 bigsheepNo = i; // 纪录第i只羊的编号 } } // 循环结束 // 输出最肥羊的重量 cout<< "最肥羊的重量为"<<bigsheep<<endl; // 输出该羊的编号 cout<< "最肥羊的编号为"<<bigsheepNo<<endl; return 0; }
数组的定义 类型说明符 数组名 [ 常量表达式 ] 例: float sheep[10]; int a2001[1000]; 说明 • 1. 数组名的第一个字符应为英文字母; • 2. 用方括号将常量表达式括起; • 3. 常量表达式定义了数组元素的个数;
4. 数组下标从 0 开始。如果定义 5 个元素,是从第 0 个元素至第 4 个元素; 例如 int a[5] 定义了5个数组元素如下: a[0], a[1], a[2], a[3], a[4] 这是 5 个带下标的变量,这5个变量的类型是相同的
5.常量表达式中不允许包含变量 例如 int n; n = 5; int a[n]; 不合法! 因为 n 是变量,不是常量
#define N 100 //宏定义,N为常数100 #define M 200 //宏定义,M为常数200 int a[ N ] ; //定义有100个元素的整型数组a long b[ N+M ] ; //定义有300个元素的长整型数组b double g[ M+6 ] ; //定义有206个元素的双精度实 //型数组g 以上定义是合法的 #define N 100 为命令行,不是语句,程序在 编译时遇到N 就用100替换。在命令行中定义的符号名N,也被称为符号常量N。
数组初始化 第一种方法 直接在定义时初始化 例如 int a[5] = { 3, 5, 4, 1, 2 }; a[0] = 3; a[1] = 5; a[2] = 4; a[3] = 1; a[4] = 2;
第二种方法 使用 memset 函数 格式为 memset(数组名, 初始化值, sizeof(数组名)) 举例: memset(sheep, 0, sizeof( sheep )); 含义是将名为 sheep 的数组中的全部元素均初始化为 0 。更深一层是说让系统为 sheep[10] 所分配的内存单元从 sheep[0] 为标志的地址单元到该数组的全部地址单元都赋以 0 。调用此库函数需要加入头文件 <memory.h> 。
请自己上机做7个实验 • 1.#include <iostream> using namespace std; int main() { int a[4]; // 声明项 cout << a[0] << endl; cout << a[1] << endl; cout << a[2] << endl; cout << a[3] << endl; return 0; } • 2.其他不变,改变声明项为 int a[4] = { 0, 1, 2, 3 };
3.其他不变,改变声明项为 int a[4] = { 3, 8 }; • 4.其他不变,改变声明项为 int a[4] = { 2, 4, 6, 8, 10 }; • 5.其他不变,改变声明项为 int a[4] = { 2, 4, 6, d }; • 6.其他不变,改变声明项为 int d; int a[4] = { 2, 4, 6, d }; • 7.其他不变,改变声明项为 int n=4; int a[n] = { 0, 1, 2, 3 };
讨 论 问 题 使用筛法求100以内的所有素数
思路 想象将100个数看作沙子和小石头子,让小石头子权称素数;让沙子当作非素数。弄一个筛子,只要将沙子筛走,剩下的就是素数了。 非素数一定是 2、3、4 ……的倍数。 使用数组,让下标就是100以内的数,让数组元素的值作为筛去与否的标志。比如筛去以后让元素值为1。
方法的依据: 1至100这些自然数可以分为三类: • 单位数:仅有一个数1。 • 素数: 是这样一个数,它大于1,且只有1 和它自身这样两个正因数。 • 合数: 除了1和自身以外,还有其他正因数。 1不是素数,除1以外的自然数,当然只有素数与合数。筛法实际上是筛去合数,留下素数。
为了提高筛法效率,注意到: 令 n 为合数(这里是100), c 为 n 的最小正因数, 据初等数论,只要找到 c 就可以确认 n 为合数,将其筛去。 一定注意:要进行“筛”的1—100的数字是与数组prime[101]的下标相对应的,而每个数组元素的取值只有2个:是0或1,分别代表(标志)与下标相对应的数字是素数或不是素数。
程序框图如下: 请同学来分析左边程序的结构 从而了解算法的设计思路 为程序代码的实现创造条件
上述框图很清晰地描述了筛法的思路: • 1.第一块是一个计数型的循环语句,功能是将prime数组清零。 prime[c] = 0; c = 2, 3,… ,100 • 2.第二块是正因数d 初始化为 d = 2。 • 3.第三块是循环筛数。这里用了一个 do while语句,属于一种直到型循环,其一般形式为: do { 循环体语句块 } while ( 表达式 )
do { 循环体语句块; } while ( 表达式 )
直到型循环框图如下: 直到表达式为假 时才退出循环,所以循环体至少执行一次。
举 例 求 π 的近似值
例.求π的近似值 用变量pi表示π的值。 令 表示括号中的每个项 当最后一项的绝对值小于等于 时,忽略掉以后的项
//************************************ //* 程 序 名:5_2.cpp * //* 作 者:wuwh * //* 编制时间:2002年9月20日 * //* 主要功能:求pi的近似值 * //************************************ #include <iostream> #include <cmath> using namespace std; int main() // 主函数 { int sum=0; // 整型变量,总项数 float pi=0.0, a=1.0, b=1.0, c=1.0; // 浮点变量,a为分母,b为分子,c为b除以a
do // 直到型循环 { // 循环体,开始 pi = pi + c; // 累加每一项 sum = sum + 1; a = a + 2.0f; // 计算每一项的分母;强制将2.0转换成float型 b = -b; // 分子变正负号 c = b / a; // 计算每一项 } // 循环体结束 while ( fabs(c) > 1e-6 ); // 当c的绝对值大于10的-6次方时,继续 // 执行循环体,否则退出 pi = 4.0f * pi; // 得到最终结果;将4.0作为float类型 cout << “pi= ” << pi << endl; // 输出pi值 cout << “sum=” << sum << endl; // 输出总项数 return 0; }
do // 直到型循环 { // 循环体,开始 pi = pi + c; // 累加每一项 sum = sum + 1; a = a + 2.0f; // 计算每一项的分母 b = -b; // 分子变正负号 c = b / a; // 计算每一项 } // 循环体结束 while ( fabs(c) > 1e-6 );
运行结果pi = 3.14159, sum = 500000 提问:这种循环当表达式的值永远为真时, 会如何? 答:会构成死循环,即无休止地执行循环体 请实验: • 1. 将 b 定义为 int 型看看执行结果并分析为什么 • 2. 将 1e-6 变为 1e-7 或 1e-4 看看结果
下面还要介绍另一种循环“当循环” 一般形式: while ( 表达式 ) { 语句块;(循环体) }
while( 表达式 ) { 语句块 ( 循环体 ); }
思考 #define N 1 while ( N ) { cout<<“ welcome to Tsinghua \n“; } 程序运行后会出现什么情况?
思考 #define N 1 while ( N - 1 ) { cout<<“ welcome to Tsinghua \n“; } 程序运行后会出现什么情况?
举 例 求两个整数的最小公倍数
分析:假定有x ,y 且 x > y,设最小公倍数为 z • 1. z 一定会 >= x • 2. z = k x , k= 1, 2, … • 3. z 一定会被 y 整除 用两个最简单的数试一下就可以找到算法. 比如 x=5, y=3.
第一步 z = x // x=5 5 % 3 != 0 // z % y 不能整除 • 第二步 z = z + x 10 % 3 != 0 // z % y不能整除 • 第三步 z = z + x 15 % 3 == 0 // z % y 能整除 找到了 z ,15就是5和3的最小公倍数
//************************************ //* 程 序 名:5_3.cpp * //* 作 者:wuwh * //* 编制时间:2002年9月20日 * //* 主要功能:求两个数的最小公倍数 * //************************************ #include <iostream> #include <cmath> using namespace std; int main() // 主函数 { int x=0, y=0, z=0, w=0; // 整型变量 cout << “请输入两个整数,用空格隔开:”; // 提示信息 cin >> x; // 键盘输入整数 x cin >> y; // 键盘输入整数 y if ( x < y ) // 让 x 表示两者中的大数 { w = x; x = y; y = w; // “倒油瓶”方法 } z = x; // 将一个大数赋给 z while ( z % y != 0 ) // 当z不能被y整除时,就让z累加x {z = z + x; } cout << “最小公倍数为” << z << endl; // 输出最小公倍数 return 0; }
cout << “请输入两个整数,用空格隔开:”; // 提示信息 cin >> x; // 键盘输入整数 x cin >> y; // 键盘输入整数 y if ( x < y ) // 让 x 表示两者中的 大数 { w = x; x = y; y = w; }
z = x; // 将一个大数赋给 z // 当z不能被y整除时,就让z累加x while ( z % y != 0 ) { z = z + x; } cout << "最小公倍数为" << z << endl;
自学与比较 请同学们去比较三种循环的异同之处 • 1. for 循环(计数型循环) • 2. 当型循环(while循环) • 3. 直到型循环(do while 循环) 上机将筛出素数的程序完成
练习: 题目:某选手 10000m 跑的每 400m 用时记录在名为 RUN 的数组中,请你计算平均用时(每400 m 的平均用时),要求使用 3 种循环语句来编程。 1. for 2. do … while 3. while
数组中已有25圈的每一圈的时间 run 0 70 71 73 72 . . . 69 0 1 2 3 4 . . . 25
int main() { int run[26]={ 70,71,… }; int sum=0 ,average=0; for( int i=1;i<=25;i++) { sum=sum+run[i]; } average=sum/25; cout<<average; system("pause"); return 0; } 初值 终值 增值
i=1; 初值 while( i <= 25 ) { 终值 sum=sum+ run[ i ]; i = i +1; 增值 }
i=1;初值 do { sum=sum+ run[ i ]; i = i +1; } while( i <= 25 );
结论 初值,终值,增值 循环中的3个表达式 一个也不能少!
a 1 8 3 2 4 9 下标 1 2 3 4 5 6 希望排成: a 9 8 4 3 2 1 下标 1 2 3 4 5 6