程序设计与算法 | (7) break, continue及循环例题

本专栏主要基于北大郭炜老师的程序设计与算法系列课程进行整理,包括课程笔记和OJ作业。该系列课程有三部分: (一) C语言程序设计;(二) 算法基础;(三) C++面向对象程序设计

(一) C语言程序设计 课程链接

1. 循环结构之break语句、continue语句

break语句
  • 可以出现在循环体中(for、while、do…while循环均可),
    其作用是跳出循环。
int n = 0; 
while(true) {
	if( n > 100)
		break;
	++n; 
}
cout << n ; //101
  • 在多重循环的情况下,break语句只能跳出直接包含它的那一重循环.

  • 如果两个不同的正整数,他们的和是他们的积的因子,就称这两个数为兄弟数,小的称为弟数,大的称为兄数。先后输入正整数n和m(n < m) , 请在n至m这m-n+1个数中,找出一对兄弟数。如果找不到,就输出“No Solution.”。如果能找到,就找出和最小的那一对;如果有多对兄弟数和相同且都是最小,就找出弟数最小的那一对。
    思路:在n-m中枚举每一对不同的数,看看是不是兄弟数。用两个变量记录当前已经找到的最佳兄弟数,如果发现更佳的,就重新记录(可以使用break加速枚举)。

 
#include <iostream>
using namespace std;
int main()  {
    int n,m;
	cin >> n >> m;
	int a = m + 1,b = m + 1; //a,b记录已经找到的最佳兄弟数,a是弟数,b是兄数,找最小初始化为最大
	for(int i=n;i<m;++i) {//取弟数,共m-n种取法
        if( i > (a + b)/2 + 1) //如果弟数已经>当前已找到最佳兄弟数和的一半 那么加上兄数肯定超过了 此时没必要再找兄数
			break; // 跳出外重循环
		for( int j = i + 1; j <= m; ++j ) { //取兄数
			if(i+j>a+b) //此时的弟数+兄数超过了最佳兄弟数和 没必要继续遍历兄数 只会越来越大
				break; //跳出内重循环 到外重循环继续遍历下一个弟数,再重新遍历兄数
			if(i*j%(i+j)==0){ //发现兄弟数
				if(i+j<a+b){ //发现和更小的兄弟数 
					a = i; b = j ; //更新已找到的最佳兄弟数
				}
				else if( i + j == a + b && i < a)//发现和相同但弟数更小的兄弟数
					 a = i; b = j; //更新已找到的最佳兄弟数
			}
		} 
	}
	if(a==m+1) //初始的a 没有被更新过 即没找到兄弟数
		cout<<"No solution.";
	else
		cout<<a<<","<<b;
	return 0;
continue语句
  • 可以出现在循环体中(for、while、do…while循环均可), 其作用是立即结束本次循环,并回到循环开头判断是否要进行下一次循环。
for( int i = 1;i <= 10 ;++i ) {
   if( i % 2 ) //奇数 非0为true
	  continue; //导致不执行后面的语句,回到循环开头 判断是否进行下一次循环
   cout << i << ","; //偶数输出 2,4,6,8,10
}
  • 在多重循环的情况下,continue只对直接包含它的那重循环起作用。

2. OJ编程题输入数据的处理

scanf表达式的值
  • scanf(…)表达式的返回值为int,表示成功读入的变量个数。
 int n,m;
 printf("%d",scanf("%d%d",&n,&m));

在这里插入图片描述

  • scanf(…) 返回值为EOF(即-1)则说明输入数据已经结束
int n,m;
while(scanf("%d%d",&n,&m) != EOF) { //EOF是直接能拿来用的符号常量
      printf("%d",n+m);
  }

Windows系统下,不停输入两个整数再敲回车,则不停输出它们的和,直到输入 Ctrl+Z 然后回车(此时scanf会返回EOF),程序结束。

或者:

int n,m; 
while(scanf("%d%d",&n,&m) == 2) {
      printf("%d",n+m);
}
cin表达式的值
  • cin >> m >> n … 表达式的值,在成功读入所有变量时为true,否则为false。
int n,m;
while(cin >> n >> m ) {
      printf("%d",n+m);
}

不停输入两个整数再敲回车,则不停输出它们的和,直到输入: Ctrl+Z 然后回车,程序结束.

处理无结束标记的OJ题目输入
  • 输入若干个(不知道多少个)正整数,输出其中的最大值
    在这里插入图片描述
#include <iostream> 
#include <cstdio> 
using namespace std; 
int main() 
{
	int n, mx = 0; //找最大初始化为最小
	while( cin >> n) {
		if( n > mx )
	    	mx = n; //更新最大
	}
	printf("%d",mx);
	return 0; 
}

或者:

#include <iostream> 
#include <cstdio> 
using namespace std; 
int main() 
{
	int n, mx = 0; 
	while(scanf("%d",&n) != EOF) {
	//或 while(scanf("%d",&n) == 1) {
		if( n > mx )
	    	mx = n;
	} 
	printf("%d",mx);
	return 0; 
}

3. 重定向输入

  • 调试程序时,每次运行程序都要从键盘手动输入测试数据,太麻烦
  • 可以将测试数据存入文件,然后用freopen将输入由键盘重定向为文件,则运行程序时不再需要手动输入数据了
#include <iostream> 
using namespace std; 
int main() 
{
	freopen("c:\\tmp\\test.txt","r",stdin); // 两个\\转义表示一个\    提交oj时 这行要删掉或注释
	//此后所有输入都来自文件 c:\tmp\test.txt  不用手动从键盘输入
	int n, mx = 0;
	while( cin >> n) {
    	if( n > mx )
        	mx = n;
	}
	printf("%d",mx); 
	return 0;
}

在这里插入图片描述

4. 循环例题选讲

  • 例1.乘方计算
    在这里插入图片描述
#include <iostream>
using namespace std;
int main()
{
	int a,n;
	cin >> a >> n;
	int result = a;  //初始化为a
	for(int i = 0;i < n-1; ++i) //n-1次 乘a
		result *= a; 
	cout << result ;
}
  • 例2. 输入若干个整数求最大值
    输入若干个整数(可正可负,不超过int的表示范围),输出最大值。
    在这里插入图片描述
#include <iostream>
using namespace std;
int main()  
{
	int n,mx; //求最大 初始化mx为最小(之前求的是正整数序列最大值,可以把mx初始化为0);现在求整数序列最大,这个初始化最小值不容易确定,此时可以把mx初始化为输入序列中的第一个数,然后遍历更新mx
	cin>>mx;
	while(cin >> n) {
		if(n>mx) {
	    	mx = n;
		}
	}
	cout << mx << endl;
	return 0;
}
  • 输入至少2个整数,求最大值和第二大值
    在这里插入图片描述
#include <iostream>
using namespace std;
int main()
{
	int n,max1,max2; //max1放第一个大的数 max2放第二大的数
	int num = 0; // 输入的是第几个数 
	while(cin >> n) {
    	++num;
        if( num == 1) //第一个数
			max1 = n;  //把最大的数初始化为第一个数
		else if( num == 2) {  //第二个数
            if( n > max1) { //第二个数>当前最大的数(第一个数)
            	max2 = max1; //把当前最大的数 赋给第二大的数
				max1 = n; //把第二个数 赋给最大的数
			}
			else
			{
				max2 = n; //把第二个数 赋给第二大的数
			}
		else{ //序列中的其他数
			if(n>=max1){
				max2 = max1;
				max1 = n;
			}
			else if(n > max2){
				max2 = n;
			}
		}
	}
	cout<<max1<<" "<<max2<<endl;
	return 0;
  • 斐波那契数列
    菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1, 接下来每个数都等于前面2个数之和。给出一个正整数k,要求菲波那契数列中第k个数是多少。
    在这里插入图片描述
#include <iostream>
using namespace std;
int main()  
{
	int a1 = 1,a2 = 1; int k;  //第1,2个数是1
	cin >> k;
	if( k == 1 || k == 2)
    	cout << 1 << endl;
    else {
		int sum;
		for(int i = 0;i < k-2; ++i) { //剩k-2个数
			sum = a1+a2; 
			a1 = a2;
			a2 = sum;
        }
        cout << a2 << endl;
	}
return 0;
}
  • 求阶乘的和
    在这里插入图片描述
    最简单的解法是用两重循环,外重循环从1~n遍历每个数i,内重计算i!。
#include <iostream> 
using namespace std; 
int main() 
{
	int n;
	cin >> n;
	int sum = 0;
	for(int i = 1;i <= n; ++i) {
		int factorial=1; //存放i阶乘 
		for(int j = 1; j <= i; ++j)
			factorial *= j; //此操作做1+2+3+...+n次  O(n^2)
		sum += factorial;
	}
	cout << sum ; 
	return 0;
}

更快的解法只需要一重循环。

#include <iostream> 
using namespace std; 
int main() {
	int n;
	cin >> n;
	int sum = 0;
	int factorial = 1;
	for(int i = 1;i <= n; ++i) {
		factorial *= i; 
		sum += factorial; //此操作做n次 O(n)
	}
	cout << sum ; 
	return 0;
}
  • 输入正整数n(n>=2),求不大于n的全部质数

解法1:

#include <iostream> 
using namespace std; 
int main() 
{
	int n;
	cin >> n;
	for(int i = 2; i <= n; ++i) { //每次判断i是否是质数
		int k;
	    for(k = 2; k < i; ++k) {
			if( i % k == 0)  //除了1和本身还有别的因子 就不是质数
				break;
		}
		if(k==i) //说明没有执行过break 
			cout<<i<<endl;
	}
	return 0;
}	

此解法做了没必要的尝试, k 大于 i的平方根后就不必再试.(如果当k小于i的平方根时,没有因子,那么k大于i的平方根必然也没有,如果有的话,就说明k小于i的平方根时有一个和他对应的另一个因子,矛盾)

#include <iostream> 
using namespace std;
int main() {
	int n;
	cin >> n; //n>=2
	cout << 2 << endl; //第一个质数
	for(int i = 3; i <= n; i+=2) { //每次判断i是否是质数 从3开始只需要判断奇数就好了 偶数(>3)一定不是质数
		int k;
		for(k = 3; k < i; k+=2) { //只需要判断奇数即可
        	if( i % k == 0) //除了1和本身有因子 则不是质数 
            	break; //跳出 
			if( k*k > i)  //k大于i的平方根时不用做尝试了 即k*k>i 也可以用cmath库中的sqrt
				break; //跳出
        }
        if( k*k > i)  //如果是第二种情况跳出 i是质数
        	cout << i << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值