数据结构前传–C–06
循环结构
循环结构:循环次数、循环体
专题一:通过循环变量控制循环次数
例如:
任意输入10个正整数,分别输出奇数、偶数的个数
统计个数:(计数器)
计数器一定要初始化0
(循环次数确定----10次)
- 尽可能使用算术表达式作条件
int x;
int cntodd = 0;// 奇数个数
int cnteven = 0;// 偶数个数计数器
for(int i = 0;i < 10;++i)// 循环10次
{
scanf("%d",&x);
if(x%2)// 输入为奇数
{
cntodd++;
}
else//输入为偶数
{
cnteven++;
}
}
printf("奇数个数=%d,偶数个数=%d\n",cntodd,cnteven);
- 要学会简化结构,学会减少分支
int x;
int cntodd = 0;// 奇数个数计数器
for(int i = 0;i < 10;++i)// 循环10次
{
scanf("%d",&x);
if(x%2)// 输入为奇数
{
cntodd++;
}
/*
因为要判断10个数里面奇数和偶数的个数,不是奇数就是偶数,因此可以只计数奇数的个数,通过总数减奇数个数来表示偶数个数,以此减少一条分支
*/
}
printf("奇数个数=%d,偶数个数=%d\n",cntodd,10-cntodd);
- 尽可能减少条件的使用(逻辑抽象)
int x;
int cntodd = 0;// 奇数个数计数器
for(int i = 0;i < 10;++i)// 循环10次
{
scanf("%d",&x);
cntodd = cntodd + x%2;
/*
如果x是奇数,cntodd奇数计数器加1,如果x是偶数,cntodd奇数计数器不加1,也就是说cntodd加0,那么,如果x是奇数,x%2为1,如果x为偶数,那么x%2为0,因此,此处可去掉条件语句,直接对cntodd加x%2
*/
}
printf("奇数个数=%d,偶数个数=%d\n",cntodd,10-cntodd);
例如:
任意输入一个正整数n(n>1),输出:1+2+3+···+n
求和:(累加器)
累加器一定要初始化0
(循环次数确定----n次)
int n,sum = 0;
scanf("%d",&n);
// 构建一个n次的循环
for(int i = 1;i < n+1;++i)// 尽可能不要写成双闭,最好是左闭右开
{
sum = sum + i;
}
printf("%d\n",sum);
- 在程序中尽可能多的使用数学公式,降低时间复杂度,提高代码效率,但尽量少的使用系统提供的数学函数(例如:sqrt(求平方根),pow(求指数))
如果怕老师会看不懂,可添加注释说明
int n,sum = 0;
scanf("%d",&n);
printf("sum=%d\n",n*(n+1)/2);
例如:
任意输入10个正整数,输出最大值及其个数
怎么求最大值:
例如:
3,2,5,8,4,7,6,9求最大值
求最大值的方法是固定的
1. 三个变量:x,max,cnt 1. x:用来一个一个的接收输入的值 2. max:用来放最大值 3. cnt:用来放当前最大值的个数 2. 当x变为下一个值时,因为此时max已经为上一轮的最大值,故将x和max进行比大小,如果x比max小,则max不用变,cnt也不用变,直接进行下一轮,如果x比max大,则max更新为此时的x即当前的最大值,cnt重新变为1,继续下一轮,当x接收到的值和max当前值相等时,说明又遇到一个最大值,此时cnt加1
- 不要将一个循环割裂成两个过程,尽量将所有的过程统一到一个循环里面去,不要将某一个数,某一部分单独割裂出来
int x;
int max,cnt;
for(int i = 0;i < 10;++i)// 循环10次
{
scanf("%d",&x);
if(i == 0)// 第一次进循环,x接收第一个数
{
max = x;
cnt = 1;
}
else// x接收的不是第一个数,即max、cnt已经有值
{
if(x > max)
{
max = x;
cnt = 1;
}
else// x <= max
{
if(x == max)
{
cnt++;
}
}
}
}
// 时间复杂度O(n)
因此上面的要比下面的更好一点
int x,max,cnt;
scanf("%d",&x);// 将第一次初始化在循环外面
max = x;
cnt = 1;
for(i = 1;i < 10;++i)// 循环9次
{
scanf("%d",&x);
if(x > max)
{
max = x;
cnt = 1;
}
else
{
if(x == max)
{
cnt++;
}
}
}
// 时间复杂度O(n)
并且,对于顺序表等问题,会不自觉地将其割裂成多份,这样时间复杂度会变高
int a[10] = {3,2,3,5,4,5,8,2,8,8};
int max,cnt;
max = a[0];
for(int i = 1;i < 10;++i)// 找最大值
{
if(a[i] > max)
{
max = a[i];
}
}
cnt = 0;
for(int i = 0;i < 10;++i)// 数最大值个数
{
if(a[i] == max)
{
cnt++;
}
}
//时间复杂度O(2n)
将求最大值和统计个数压缩合成到一起
int a[10] = {3,2,3,5,4,5,8,2,8,8};
for(int i = 0;i < 10;++i)// 循环10次
{
if(i == 0)// 第一次循环
{
max = a[i];
cnt = 1;
}
else
{
if(a[i] > max)
{
max = a[i];
cnt = 1;
}
else
{
if(a[i] == max)
{
cnt++;
}
}
}
}
// 时间复杂度O(n)
专题二:查找区间内满足条件的值(区间值作条件)
例如:
输出100~999之间的水仙花数(穷举法)
(穷举法效率低)
(水仙花数:abx = a^3 + b^3 + c^3)
- 尽可能不要使用穷举法,除非这个题只能用穷举法,或者实在想不出来更好的办法时,用穷举法
// 本题水仙花数毫无规律可言,因此只能使用穷举法
for(x = 100;x < 1000;++x)// 100循环到999
{
t1 = x%10;// 拆分个位数
t2 = x/10%10;// 拆分十位数
t3 = x/100;// 拆分百位数
if(x == t1*t1*t1 + t2*t2*t2 + t3*t3*t3)// 尽量不要用到系统函数pow()等
{
printf("%5d",x);
}
}
例如:
输出100~999之间的完全平方数
平方根:10^2 ~ 31^2
(完全平方数:是某个数的平方,且至少有两位数字相同)
for(int x = 100;x < 1000;++x)// 先把循环架出来
{
t1 = x%10;// 拆分个位数
t2 = x/10%10;// 拆分十位数
t3 = x/100;// 拆分百位数
if(t1 == t2 || t2 == t3 || t3 == t1)// 至少有两位数字相同的数进来
{
for(int k = 10;k < 32;++k)// 穷举法
{
if(x == k*k)
{
printf("%5d",x);
}
}
}
}
// 时间复杂度O(n^2)
// 循环次数(900x22)
- 必须要用穷举法时,要尽可能简化结构
for(int k = 10;k < 32;++k)// 只循环平方根,通过平方根的平方来寻找
{
x = k*k;// x是某个数的平方在100~999内的数
t1 = x%10;// 拆分个位数
t2 = x/10%10;// 拆分十位数
t3 = x/100;// 拆分百位数
if(t1 == t2 || t2 == t3 || t3 == t1)
{
printf("%5d",x);
}
}
// 时间复杂度O(n)
// 循环次数(22)
例如:
男人、女人、小孩吃饭问题:
man:3¥/人
woman:2¥/人
kid:1¥/人
要求去够30个人,花够50元钱
问:man、woman、kid各有多少人?
假设男人x,女人y,小孩z,
列方程组:3x+2y+z = 50,x+y+z = 30
两个方程组无法求解三个未知量
因此,
无法通过已有知识建立公式模型
只能穷举
男人最多去16个,女人最多去25个,小孩最多去30个
男人去0个,女人就对相应可以去0 ~ 25个,女人去0个,小孩就可以去0 ~ 30个,以此映射,嵌套
for(x = 0;x < 17;++x)// 男人从0~16取
{
for(y = 0;y < 26;++y)// 女人从0~25取
{
for(z = 0;z < 31;++z)// 小孩从0~30取
{
if(x+y+z == 30 && 3*x+2*y+z == 50)
{
printf("%5d%5d%5d\n",x,y,z);
}
}
}
}
// 时间复杂度O(n^3)
// 循环次数(17*26*31)
简化结构:
for(x = 0;x < 17;++x)// 确定男人
{
for(y = 0;y < 26;++y)// 确定女人
{
z = 30-x-y;// 确定z,并保证x+y+z=30
// 减法要考虑范围,因为两数相减可能产生负值
if(z >= 0 && 3*x+2*y+z == 50)
{
printf("%5d%5d%5d\n",x,y,z);
}
}
}
// 时间复杂度O(n^2)
// 循环次数(17*26)
再次简化结构:
for(x = 0;x < 17;++x)// 确定男人
{
y = 20-2*x;// 确定女人,因为有两个三元一次方程组,所以可以消掉一个元
z = 30-x-y;// 确定小孩
// 减法要考虑范围,因为两数相减可能产生负值
if(y >= 0 && z >= 0 && 3*x+2*y+z == 50)
{
printf("%5d%5d%5d\n",x,y,z);
}
}
// 时间复杂度O(n)
// 循环次数(17)
注意:
减法:一定要考虑取值范围,两数相减出现负值时,可能不符合题意,干扰结果。
总结
本文仅是个人考研备考所作的课堂笔记,多处内容仅贴合个人的学习习惯,若带来什么阅读上的不良体验,还望海涵。