1. 分支结构
1.1 单分支控制
if(条件)
{ 语句块1; }
如果条件成立,则执行语句块1,否则直接执行后继语句,语句块1被忽略,执行后继语句。
例3.1:输入两个数m和n,如果m<n,则交换两个数。
#include<iostream>
using namespace std;
int main()
{
int m,n;
cin>>m>>n;
if(m<n)
{
int t=m;
m=n;
n=t;
}
cout<<"After if statement"<<endl;
}
- 第7行为条件判断,如果m<n,则执行第9-11行,交换m和n的值。这个交换操作是C++中的基本操作;如果条件不成立,则跳过第8-12行,直接执行第13行。
- 第7行的右小括号右侧不能加分号,因为语句未结束。如果添加了分号,表示条件成立时执行空语句(单个分号表示空语句),第8-12行与第7行的if语句就没有任何关联性了。
- if语句后面只能接一条语句或一个语句块,如果需要执行多条语句,就用大括号将多条语句封装成语句块。初学者如果分不清什么时候加大括号,可以在任何时候都加大括号,这样语法上总是正确的。
- 真正的程序员必须保证代码具有良好的缩进,这里的if,以及后面将要提及的else,或循环语句,其关联的语句块都要保持缩进状态,这样可以非常快速的了解程序的层次关系。
1.2 双分支控制
如果条件成立,则执行语句块1,否则执行语句块2,二者必然有一项要被执行。if(条件)
{ 语句块1; }
else
{ 语句块2; }
else后面不写条件,但是具有隐含条件,即if的条件不成立。
例3.2:输入一个整数m,判断m的奇偶性。
#include<iostream>
using namespace std;
int main()
{
int m;
cin>>m;
if(m%2==0)
cout<<"偶数"<<endl;
else
{
cout<<"奇数"<<endl;
}
}
- 这里if和else接的语句都是单条语句,可以像第8行那样不加大括号,也可以像第11行那样增加大括号。
根据整型和布尔型之间的对应关系,以上程序也可以改写为:
#include<iostream>
using namespace std;
int main()
{
int m;
cin>>m;
if(m%2)
cout<<"奇数"<<endl;
else
cout<<"偶数"<<endl;
}
进一步可以改写为问号表达式:条件?条件成立语句:条件不成立语句
。首先进行条件判断,条件成立时执行冒号前的语句,否则执行冒号后的语句。这是C/C++中的三目运算符,极大简化了双分支结构的书写。
#include<iostream>
using namespace std;
int main()
{
int m;
cin>>m;
cout<<(m%2?"奇数":"偶数")<<endl;
}
1.3 多分支控制
if(条件1)
{ 语句块1; }
else if(条件2)
{ 语句块2; }
else
{ 语句块3; }
如果条件1成立,则执行语句块1,否则执行条件2的判断,如果条件2成立,执行语句块2,否则执行语句块3。必然有一个分支要被执行。
- else具有隐含条件,第3行的else表示条件1不成立,第5行的else表示条件1和条件2都不成立。因此不要在条件2中去判断条件1不成立,因为一旦执行到第4-6行,表示条件1肯定是不成立的,不需要判断。
- C/C++提供switch结构进行多分支,但是其执行效率与else if的多分支相同,不建议使用switch。
例3.3:编写一个程序,根据用户输入的期末考试成绩,输出相应的成绩评定信息。成绩大于等于90分输出“优”;成绩大于等于80分小于90分输出“良”;成绩大于等于60分小于80分输出“中”;成绩小于60分输出“差”。
#include<iostream>
using namespace std;
int main()
{
int score;
cin>>score;
int grade=score/10;
if(grade>=9)
cout<<"优秀"<<endl;
else if(grade==8)
cout<<"良好"<<endl;
else if(7==grade)
cout<<"一般"<<endl;
else if(6==grade)
cout<<"及格"<<endl;
else
cout<<"不及格"<<endl;
}
- 这里用到了整除特性,将百分制转换为10个区间,然后用多分支划分为5个区域。
- 第10行的
==
如果误写为=
,则程序流程为先将grade
赋值为8,然后判断grade
的布尔值。因为8为非零数,总是代表true
,所以全部小于90的分数都会被输出良好。因此要特别注意==
和=
的区别。 当判断变量和常量是否相等时,建议把常量写在左侧,如果
==
被误写为=
,程序编译时会报错,因为常量不能被赋值。如第12行和第14行所示。1.4 分支嵌套
if(条件1)
{
if(条件2)
{ 语句块2; }
else
{ 语句块3; }
}
else
{ 语句块4; }
每个else 部分总是与它前面最近的那个缺少对应的 else 部分的 if 语句配对。
- 建议使用大括号避免二义性。
else具有隐含条件,第3行的else表示条件1不成立,第5行的else表示条件1和条件2都不成立。因此不要在条件2中去判断条件1不成立,因为一旦执行到第4-6行,表示条件1肯定是不成立的,不需要判断。
例3.4:将例3.3改写为分支嵌套结构。
#include<iostream>
using namespace std;
int main()
{
int score;
cin>>score;
if(score>=60)
{
if(score<70)
cout<<"及格"<<endl;
else if(score<80)
cout<<"一般"<<endl;
else if(score<90)
cout<<"良好"<<endl;
else
cout<<"优秀"<<endl;
}
else
cout<<"不及格"<<endl;
}
- 以第11行为例,不要将其写成
else if(score>=70 && score<80)
,这里的else
代表的含义就是score>=70
,不需要重复表达。2. 分支程序优化
程序设计是一种思维训练,每个程序都尽量的进行优化,这样可以锻炼思维,让程序达到最优。以两个样例探讨分支程序的优化。2.1 三天打鱼两天晒网
例3.5:中国有句俗语叫“三天打鱼两天晒网”。假设某人从某天起,开始三天打鱼两天晒网”,问这个人在以后的第N天中是”打鱼“还是晒网”?
【输入】
输入在一-行中给出1个不超过1000的正整数N。
【输出】
在一行中输出此人在第N天中是’Fishing” (打鱼”)还是“Drying” (晒网”),并且输出”on day N”。
【问题分析】第1-3天打鱼,第4-5天晒网,然后不断循环这个过程。这种数据的周期性最适合用取模运算进行解决。因此有了第一个解决方案:
【样例输入1】103#include<iostream>
using namespace std;
int main()
{
int day;
cin>>day;
if (day%5==1||day%5==2||day%5==3)
cout<<"Fishing on day "<<day<<endl;
else
cout<<"Drying on day "<<day<<endl;
return 0;
}
【样例输出1】
Fishing on day 103
【样例输入2】
34
【样例输出2】
Drying on day 34
考虑晒网的天数少,可以书写上减少一个条件,因此第7-10行可以改写为:
连续数字可以用范围表示,但是0和4在这个范围的两侧,不好归结。这时就可以考虑数学变换。第7-10行可以改写为:if (day%5==4||day%5==0)
cout<<"Drying on day "<<day<<endl;
else
cout<<"Fishing on day "<<day<<endl;
最后,还可以用问号表达式进行书写优化。if ((day-1)%5<3)
cout<<"Fishing on day "<<day<<endl;
else
cout<<"Drying on day "<<day<<endl;
cout<<((day-1)%5<3?"Fishing":"Drying")<<" on day "<<day<<endl;
2.2 虫子吃苹果
例3.6:你买了一箱n个苹果,很不幸的是买完时箱子里混进了一条虫子。虫子每x小时能吃掉一个苹果假设虫子在吃完一个苹果之前不会吃另 一个,那么经过y小时你还有多少个完整的苹果?
【输入】
输入仅一行,包括n, x和y (均为整数)
【输出】
输出仅一行,剩下的苹果个数
【样例输入1】10 4 9#include<iostream>
using namespace std;
int main()
{
int n,x,y;
cin>>n>>x>>y;
if (y%x==0)
cout<<(n-y/x)<<endl;
else
cout<<(n-y/x-1)<<endl;
return 0;
}
【样例输出1】
7
程序思想非常简单,如果y是x的倍数,则直接得到结果,否则需要多减一天。这里实际上是一种向上取整的方法,可以通过数学进行优化。因为x和y都是整数,所以最小的差距是1。如果在y的基础上增加x-1,那么只要有余数,被吃掉苹果的数量就会加1,如果正好整除,x-1会被整除操作自动舍弃。以下代码优化后去掉了分支操作。
C/C++中提供了ceil函数进行向上取整。书写上比数学方法麻烦,但是更容易理解。#include<iostream>
using namespace std;
int main()
{
int n,x,y;
cin>>n>>x>>y;
cout<<(n-(y+x-1)/x)<<endl;
return 0;
}
总而言之,可以通过语法、数学方法或库函数对程序进行优化。#include<iostream>
#include<cmath> //支持ceil函数的定义
using namespace std;
int main()
{
int n,x,y;
cin>>n>>x>>y;
cout<<(n-ceil(y/(float)x))<<endl; //通过强制类型转换,把整除改为浮点数除法
return 0;
}