1. 分支结构

1.1 单分支控制

  1. if(条件)
  2. { 语句块1; }

如果条件成立,则执行语句块1,否则直接执行后继语句,语句块1被忽略,执行后继语句。
例3.1:输入两个数m和n,如果m<n,则交换两个数。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int m,n;
  6. cin>>m>>n;
  7. if(m<n)
  8. {
  9. int t=m;
  10. m=n;
  11. n=t;
  12. }
  13. cout<<"After if statement"<<endl;
  14. }
  • 第7行为条件判断,如果m<n,则执行第9-11行,交换m和n的值。这个交换操作是C++中的基本操作;如果条件不成立,则跳过第8-12行,直接执行第13行。
  • 第7行的右小括号右侧不能加分号,因为语句未结束。如果添加了分号,表示条件成立时执行空语句(单个分号表示空语句),第8-12行与第7行的if语句就没有任何关联性了。
  • if语句后面只能接一条语句或一个语句块,如果需要执行多条语句,就用大括号将多条语句封装成语句块。初学者如果分不清什么时候加大括号,可以在任何时候都加大括号,这样语法上总是正确的。
  • 真正的程序员必须保证代码具有良好的缩进,这里的if,以及后面将要提及的else,或循环语句,其关联的语句块都要保持缩进状态,这样可以非常快速的了解程序的层次关系。

    1.2 双分支控制

    1. if(条件)
    2. { 语句块1; }
    3. else
    4. { 语句块2; }
    如果条件成立,则执行语句块1,否则执行语句块2,二者必然有一项要被执行。

    else后面不写条件,但是具有隐含条件,即if的条件不成立。

例3.2:输入一个整数m,判断m的奇偶性。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int m;
  6. cin>>m;
  7. if(m%2==0)
  8. cout<<"偶数"<<endl;
  9. else
  10. {
  11. cout<<"奇数"<<endl;
  12. }
  13. }
  • 这里if和else接的语句都是单条语句,可以像第8行那样不加大括号,也可以像第11行那样增加大括号。

根据整型和布尔型之间的对应关系,以上程序也可以改写为:

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int m;
  6. cin>>m;
  7. if(m%2)
  8. cout<<"奇数"<<endl;
  9. else
  10. cout<<"偶数"<<endl;
  11. }

进一步可以改写为问号表达式:条件?条件成立语句:条件不成立语句。首先进行条件判断,条件成立时执行冒号前的语句,否则执行冒号后的语句。这是C/C++中的三目运算符,极大简化了双分支结构的书写。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int m;
  6. cin>>m;
  7. cout<<(m%2?"奇数":"偶数")<<endl;
  8. }

1.3 多分支控制

  1. if(条件1)
  2. { 语句块1; }
  3. else if(条件2)
  4. { 语句块2; }
  5. else
  6. { 语句块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分输出“差”。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int score;
  6. cin>>score;
  7. int grade=score/10;
  8. if(grade>=9)
  9. cout<<"优秀"<<endl;
  10. else if(grade==8)
  11. cout<<"良好"<<endl;
  12. else if(7==grade)
  13. cout<<"一般"<<endl;
  14. else if(6==grade)
  15. cout<<"及格"<<endl;
  16. else
  17. cout<<"不及格"<<endl;
  18. }
  • 这里用到了整除特性,将百分制转换为10个区间,然后用多分支划分为5个区域。
  • 第10行的==如果误写为=,则程序流程为先将grade赋值为8,然后判断grade的布尔值。因为8为非零数,总是代表true,所以全部小于90的分数都会被输出良好。因此要特别注意===的区别。
  • 当判断变量和常量是否相等时,建议把常量写在左侧,如果==被误写为=,程序编译时会报错,因为常量不能被赋值。如第12行和第14行所示。

    1.4 分支嵌套

    1. if(条件1)
    2. {
    3. if(条件2)
    4. { 语句块2; }
    5. else
    6. { 语句块3; }
    7. }
    8. else
    9. { 语句块4; }
  • 每个else 部分总是与它前面最近的那个缺少对应的 else 部分的 if 语句配对。

  • 建议使用大括号避免二义性。

    else具有隐含条件,第3行的else表示条件1不成立,第5行的else表示条件1和条件2都不成立。因此不要在条件2中去判断条件1不成立,因为一旦执行到第4-6行,表示条件1肯定是不成立的,不需要判断。

例3.4:将例3.3改写为分支嵌套结构。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int score;
  6. cin>>score;
  7. if(score>=60)
  8. {
  9. if(score<70)
  10. cout<<"及格"<<endl;
  11. else if(score<80)
  12. cout<<"一般"<<endl;
  13. else if(score<90)
  14. cout<<"良好"<<endl;
  15. else
  16. cout<<"优秀"<<endl;
  17. }
  18. else
  19. cout<<"不及格"<<endl;
  20. }
  • 以第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. #include<iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. int day;
    6. cin>>day;
    7. if (day%5==1||day%5==2||day%5==3)
    8. cout<<"Fishing on day "<<day<<endl;
    9. else
    10. cout<<"Drying on day "<<day<<endl;
    11. return 0;
    12. }
    【样例输入1】103
    【样例输出1】
    Fishing on day 103
    【样例输入2】
    34
    【样例输出2】
    Drying on day 34
    考虑晒网的天数少,可以书写上减少一个条件,因此第7-10行可以改写为:
    1. if (day%5==4||day%5==0)
    2. cout<<"Drying on day "<<day<<endl;
    3. else
    4. cout<<"Fishing on day "<<day<<endl;
    连续数字可以用范围表示,但是0和4在这个范围的两侧,不好归结。这时就可以考虑数学变换。第7-10行可以改写为:
    1. if ((day-1)%5<3)
    2. cout<<"Fishing on day "<<day<<endl;
    3. else
    4. cout<<"Drying on day "<<day<<endl;
    最后,还可以用问号表达式进行书写优化。
    1. cout<<((day-1)%5<3?"Fishing":"Drying")<<" on day "<<day<<endl;

    2.2 虫子吃苹果

    例3.6:你买了一箱n个苹果,很不幸的是买完时箱子里混进了一条虫子。虫子每x小时能吃掉一个苹果假设虫子在吃完一个苹果之前不会吃另 一个,那么经过y小时你还有多少个完整的苹果?
    【输入】
    输入仅一行,包括n, x和y (均为整数)
    【输出】
    输出仅一行,剩下的苹果个数
    1. #include<iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. int n,x,y;
    6. cin>>n>>x>>y;
    7. if (y%x==0)
    8. cout<<(n-y/x)<<endl;
    9. else
    10. cout<<(n-y/x-1)<<endl;
    11. return 0;
    12. }
    【样例输入1】10 4 9
    【样例输出1】
    7
    程序思想非常简单,如果y是x的倍数,则直接得到结果,否则需要多减一天。这里实际上是一种向上取整的方法,可以通过数学进行优化。因为x和y都是整数,所以最小的差距是1。如果在y的基础上增加x-1,那么只要有余数,被吃掉苹果的数量就会加1,如果正好整除,x-1会被整除操作自动舍弃。以下代码优化后去掉了分支操作。
    1. #include<iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. int n,x,y;
    6. cin>>n>>x>>y;
    7. cout<<(n-(y+x-1)/x)<<endl;
    8. return 0;
    9. }
    C/C++中提供了ceil函数进行向上取整。书写上比数学方法麻烦,但是更容易理解。
    1. #include<iostream>
    2. #include<cmath> //支持ceil函数的定义
    3. using namespace std;
    4. int main()
    5. {
    6. int n,x,y;
    7. cin>>n>>x>>y;
    8. cout<<(n-ceil(y/(float)x))<<endl; //通过强制类型转换,把整除改为浮点数除法
    9. return 0;
    10. }
    总而言之,可以通过语法、数学方法或库函数对程序进行优化。