本专栏主要基于北大郭炜老师的程序设计与算法系列课程进行整理,包括课程笔记和OJ作业。该系列课程有三部分: (一) C语言程序设计;(二) 算法基础;(三) C++面向对象程序设计
1. 条件分支结构之if语句
有时,并非所有的程序语句都要被顺序执行到,会希望满足某种 条件就执行这部分语句,满足另一条件就执行另一部分语句。这就需 要“条件分支结构”
if语句
- 依次计算表达式1、表达式2…只要碰到一个表达式i为真,则执行语句组i(前面为假的表达式对应的语句组不会被执行),后面的表达式不再计算,后面的语句组也都不会被执行。
- 若所有表达式都为假,则执行语句组n
- 可以没有 else if,也可以没有 else,也可以都没有
- 如果“语句组”只有一条语句,可以不用{ }
例题
写一个判断整数奇偶性的程序,要求输入一个整数,如果是奇数,就输出“It’s odd.”,如果是偶数,就输出“It’s even.”。
#include <iostream>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
if( n % 2 == 1)
printf("It's odd.\n") ;
else
printf("It's even.\n") ;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
if( n % 2) //取余2非0 则为奇数,C++中非0整数为true
printf("It's odd.\n") ;
else
printf("It's even.\n") ;
return 0;
}
if语句嵌套
在一条if语句的某个分支(语句组)里,还可以再写if语句。
#include <iostream>
using namespace std;
int main()
{
int a;
scanf("%d",&a);
if( a > 0)
if ( a % 2 )
cout << "good";
else //这个else到底和哪个if配对?
cout << "bad";
return 0;
} //输入-1,输出?
注意在上面的c++代码里不要看对齐,else总是和离他最近的if配对。实际情况如下:
#include <iostream>
using namespace std;
int main()
{
int a;
scanf("%d",&a);
if( a > 0) //if-else语句整体是一条语句 所以此处可以不加{}
if ( a % 2 )
cout << "good";
else
cout << "bad";
return 0;
} //输入-1, 程序无输出!!!
可以使用{},改变if-else的配对,下面的代码中加了{}后,else就可以与第一个if配对了:
int main() {
int a;
scanf("%d",&a);
if( a > 0) {
if ( a % 2 )
cout << "good";
}
else
cout << "bad";
return 0;
} //输入-1,输出 bad
请写一个程序,该程序输入一个年份,根据该年份是否是建国整十周年、建党整 十周年以及是否是闰年给出不同的输出。
#include <iostream>
using namespace std;
int main()
{
int year;
scanf("%d",& year);
if( year <= 0)
printf("Illegal year.\n") ;
else {
printf("Legal year.\n");
if( year > 1949 && (year - 1949) % 10 == 0 ) //建国整10周年
printf("Luky year.\n");
else if( year > 1921 && !((year - 1921) % 10)) //建党整10周年
printf("Good year.\n");
else if( year % 4 == 0 && year % 100 || year % 400 == 0 ) //整除4且不整除100或整除400
printf("Leap year\n"); //闰年
else
printf("Common year.\n");
}
return 0;
}
if语句常见错误
int a = 0;
if( a = 0 ) //a = 0的值是0。0为假
printf("hello");
if( a = 5 ) // a = 5的值是5。非0为真
printf("Hi");
输出 Hi。
注意=是赋值,==是判断相等。
互相矛盾的多个条件,如果确实只希望执行其中一个分支,应该用if和多个else if,而不要写多个if
int a = 0;
if( a >=0 && a < 5 )
a = 8;
else if( a >= 5 && a < 10 )
cout << "hello";
else if( a > 10 && a < 20)
.....
else
.....
此时不会输出hello,只会执行一个分支。
错误写法:
int a = 0;
if( a >=0 && a < 5 )
a = 8;
if( a >= 5 && a < 10 )
cout << "hello";
if( a > 10 && a < 20)
.....
if( a >= 20)
.....
此时会输出hello, 会执行多个分支。
2. 条件分支结构之switch语句
switch语句
if( n % 5 == 0 ) {
......
}
else if(n % 5 == 1 ) {
......
}
else if( n % 5 == 2 ) {
......
}
else if( n % 5 == 3 ) {
......
}
else {
......
}
- 太多的else if 不方便
- n % 5多次计算,浪费
switch(表达式) { //表达式的值 必须是整数类型(int,char ......)
case 常量表达式1: //常量表达式必须是整数类型的常量(int,char...)
语句组1
break;
case 常量表达式2:
语句组2
break;
......
case 常量表达式n:
语句组n
break;
default:
语句组n+1
}
“表达式”(表达式只计算一次即可)的值等于哪个“常量表达式”, 就执行相应的语句组。都不相等,则执行 default的语句组。也可以没有default分支**“常量表达式” 里面不能包含变量**!
请写一个程序,接受一个整数作为输入,如果输入1,则输出“Monday” ,输入2,则输出“Tuesday”…输入7,则输出“Sunday”,输入其他数,则输出“Illegal”。
#include <iostream>
using namespace std;
int main()
{
int n;
scanf("%d",& n);
switch(n) {
case 1:
printf("Monday");
break;
case 2:
printf("Tuesday");
break;
case 3:
printf("Wednesday");
break;
case 4:
printf("Thursday");
break;
case 5:
printf("Friday");
break;
case 6:
printf("Saturday");
break;
case 7:
printf("Sunday");
break;
default:
printf("Illegal");
}
return 0;
}
switch语句在进入某个case分支后,会一直执行到第一个碰到的“break;” ,即使这个“break;”是在后面的case分支里面。如果没有碰到“break;”,则会向下一直执行到switch语句末尾的“}”,包括“default:”部分的语句组也会被执行。
#include <iostream>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
switch(n%6) {
case 0:
printf( "case 0" );
break;
case 1:
printf( "case 1" );
case 2:
case 3:
printf( "case 2 or 3" );
break;
case 4:
printf( "case 4" );
break;
}
return 0;
}
3. 循环结构之for循环
for循环
for( 表达式1 ;表达式2;表达式3){
语句组
}
1)计算“表达式1”。
2)计算“表达式2”,若其值为true,则执行“{ }”中 的语句组,然后转到3);若为false,则不再执行“{}” 中的语句组,for语句结束,转到5)。
3)计算“表达式3”。
4)转到2)。
5) 从for语句后面继续往下执行程序。
一般用于将某段代码(语句组)重复执行若干次!!!
- 连续输出26个字母
int i; //循环控制变量
for(i=0;i<26;i++){
cout<<char('a'+i); //'a'+i为字母ASCII码 然后再用char()强制转换为char类型
}
或
for(int i=0;i<26;i++) //语句组里只有一条语句就可以不用写”{ }”
printf("%c",'a'+i);
- 循环控制变量定义在"表达式1"中,则其只在for语句内部起作用,可以不用担心循环控制变量重名:
int i = 5;
for(int i = 0;i < 26; ++i )
cout << char('a'+i );
cout << endl;
for(int i = 0;i < 26; i+=2 ){ //循环控制变量并非每次只能加1
cout << char('A'+i ) ;
}
cout << endl;
cout << i; //此处的i和for里面的i无关
- for循环结构里的“表达式1”和“表达式3”都可以是用逗号连接的若干个表达式。
for(int i= 15, j = 0; i > j; i-=2 ,j+= 3)
cout << i << "," << j << endl;
- 写一个程序,输入一个正整数n,从小到大输出它的所有因子
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
for( int i = 1;i <= n; ++i)
if( n % i == 0 )
cout << i << endl;
return 0;
}
- 写一个程序,输入一个正整数n,从大到小输出它的所有因子
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
for( int i = n;i >= 1; --i)
if( n % i == 0 )
cout << i << endl;
return 0;
}
嵌套for循环
for循环可以嵌套,形成多重for循环。
for(int i = 0;i < n; ++ i) {
......
for(int j = 0; j < m; ++j ) {
...... //内重循环的执行次数一共是n×m次
}
......
}
- 给定正整数n和m,在1至n这n个数中,取出两个不同(与顺序无关)的数,使得其和是m的因子,问有多少种不同的取法。
思路:穷举1-n这n个数中取两个数的所有取法,对每一种取法,判断其和是不是m 的因子.
穷举的办法:
第一个数取1,第二个数分别取2,3,…n
第一个数取2, 第二个数分别取3,4,…n
…
第一个数取n-2,第二个数分别取n-1,n
第一个数取n-1,第二个数取n
#include <iostream>
using namespace std;
int main()
{
int n,m;
int total = 0; //取法总数
cin >> n >> m;
for( int i = 1; i < n; ++i ) {//取第一个数,共n-1种取法
for( int j = i + 1; j <= n; ++j ) //第二个数要比第一个数大,以免取法重复
if(m%(i+j)==0)
++total;
}
cout << total;
return 0;
}
for 语句括号里面的“表达式1”,“表达式2”,“表达式3”任何一个都可以不写,甚至可以全都不写,但是“;”必须保留。
for( ; i < 100; ++ i ) //假设i在for前已经有合理值
cout << i ;
for( ; ; )
cout << "hello" <<endl; //永远不停输出 hello
可以用 break 语句从 for( ; ;)死循环中跳出.
4. 循环结构之while循环、do…while循环
while循环
并非到达指定次数,而是满足某条件时即停止循环,则适合用while语句来实现循环:
while(表达式){
语句组
}
- 判断“表达式”是否为真,如果不为真,则转4)
- 执行“语句组”
- 转1)
- while语句结束,继续执行while语句后面的语句。
while(true){
语句组
} //死循环,可以用break跳出
- 输入若干个(至少1个)不超过100的正整数,输出其中的最大值、最小值以及所有数的和。输入的最后一个数是0,标志着输入结束。
#include <iostream>
using namespace std;
int main()
{
int sum = 0, maxN = 0, minN = 200,n; //初始化最大值为最小,初始化最小值为最大
cin >> n;
while( n ){
if( n > maxN)
maxN = n;
if( n < minN)
minN = n;
sum += n;
cin >> n;
}
cout << maxN << " " << minN << " " << sum;
return 0;
}
- 用牛顿迭代法求输入的数的平方根。
欲求a的平方根,首先猜测一个值x1=a/2(也可以是随便什么其他值)作为其平方根,然后根据下面的迭代公式算出x2,再将x2代入公式右边算出x3…直到连续两次算出的xn和xn+1的差的绝对值小于某个值ε,即认为找到了足够精确的平方根。这个ε值取得越小,计算出来的平方根就越精确。
迭代公式: x n + 1 = ( x n + a / x n ) / 2 x_{n+1} = (x_n+a/x_n)/2 xn+1=(xn+a/xn)/2
#include <iostream>
using namespace std;
double EPS = 0.001; //用以控制计算精度
int main()
{
double a;
cin >> a ; //输入a,要求a的平方根
if( a >= 0) {
double x = a/2, lastX = x + 1 + EPS;//确保能够进行至少一次迭代 x为当前的根 lastX为上一次计算的根
while( x - lastX > EPS || lastX - x > EPS){ //只要精度未达要求, 两个根差的绝对值大于EPS
lastX = x;
x = (x + a/x)/2;
}
cout << x;
}
else
cout << "It can't be nagitive.";
return 0;
}
do…while循环
如果希望循环至少要执行一次,就可以使用do…while语句。
do{
语句组;
}while(表达式);
每执行一次循环后,都要判断“表达式”的值是否为真,如果真就继续循环,如果为假,就停止循环。
- 输出1到10000以内所有2的整数次幂:
int n = 1;
do{
cout<<n<<endl;
n *= 2;
}while(n<10000);