C语言while循环中i的作用域,C语言学习记录(七)——分支、循环、函数、递归习题总结...

一、关于分支、循环部分习题总结

1.关于while循环的概念

while(条件表达式)

循环体

while循环中,当条件表达式成立时,才会执行循环体中的语句,每次执行期间,都会对循环因子进行修改(否则就成为死循环),修改完成后如果while条件表达式成立,继续循环,如果不成立,循环结束。故:while循环条件将会比循环体多执行一次。

2.以下程序的运行结果是?

#inclde

int main(){

int a = 0, b = 0;

for (a = 1, b = 1; a <= 100; a++){

if (b >= 20) break;

if (b % 3 == 1){

b = b + 3;

continue;

}

b = b - 5;

}

printf("%d\n", a);

return 0;

}

分析:

第一次循环:a = 1,b = 1--->b小于20, if不成立,b%3 == 1%3  == 1成立,b=b+3, 此时b的值为4

第二次循环:a = 2,b = 4--->b小于20, if不成立,b%3 == 4%3  == 1成立,b=b+3, 此时b的值为7

第三次循环:a = 3,b = 7--->b小于20, if不成立,b%3 == 7%3  == 1成立,b=b+3, 此时b的值为10

第四次循环:a = 4,b = 10--->b小于20,if不成立,b%3 == 10%3 == 1成立,b=b+3, 此时b的值为13

第五次循环:a = 5,b = 13--->b小于20,if不成立,b%3 == 13%3 == 1成立,b=b+3, 此时b的值为16

第六次循环:a = 6,b = 16--->b小于20,if不成立,b%3 == 16%3 == 1成立,b=b+3, 此时b的值为19

第七次循环:a = 7,b = 19--->b小于20,if不成立,b%3 == 19%3 == 1成立,b=b+3, 此时b的值为22

第八次循环:a = 8,b = 22--->b大于20,if成立,循环break提出

最后打印a:8

3.给定两个数,求这两个数的最大公约数

思路为:  有两个整数a和b,   ①a%b得到余数c   ②若c = 0,则b即为两数的最大公约数   ③若c ≠ 0,则a = b, b = c然后再去执行①

#include

//写法一

int main(){

int a = 0; int b = 0; int c = 0;

printf("%s:\n","请输入两个整数");

scanf("%d %d", &a, &b);

do{

c = a % b;

if (c == 0){

printf("%d 为两数的最大公约数\n", b);

}

a = b; b = c;

} while(c != 0);

}

//写法二

int main(){

int a = 18;

int b = 24;

int c = 0;

while(c = a % b){

a = b;

b = c;

}

printf("%d\n", b)

}

4.打印出1000年到2000年之间的闰年

思路为:   能被4整除,不能被100整除的年份为闰年;或者能被400整除的年份为闰年

#include

int main(){

for (int i = 1000; i <= 2000; i++){

if (i %4 == 0 && i % 100 ! = 0 || i % 400 == 0){

printf("%d年", i);

}

}

}

5.打印出100~200之间的素数,以及素数的个数

思路为:   素数:即质数,除了1和自己之外,再没有其他的约数,则该数据为素数 方法一:   判断i是否为素数:用[2, i)之间的每个数据去被i除,只要有一个可以被整除,则不是素数

#include

int main(){

int i = 0;

int count = 0;

for (i = 100; i <= 200; i++){

int j = ;

for (j = 2; j < i; j++){

if (i % j == 0){

break;

}

}

if (j == 1){

count++;

printf("%d", i);

}

}

printf("\ncount = %d\n", count);

return 0;

}

方法二:   上述方法一中,超过i的一半的数据肯定不是i的倍数,进行了许多没有意义的运算,优化为,拿到一个数据i只需要检测[2, i/2]区间内是否有元素可以被i整除,可以说明i不是素数。

#include

int main(){

int i = 0;

int count = 0;

for (i = 100; i <= 200; i++){

int j = ;

for (j = 2; j < i/2; j++){

if (i % j == 0){

break;

}

}

if (j  > i/2){

count++;

printf("%d", i);

}

}

printf("\ncount = %d\n", count);

return 0;

}

方法三:   再优化后为,如果i能够被[2, sqrt(i)]之间的任意数据整除,则i不是素数。愿意:如果 m能被2 ~ m-1之间任一整数整除,其二个因子必定有一个小于或等于sqrt(m),另一个大于或等于 sqrt(m)。

#include

int main(){

int i = 0;

int count = 0;

for(i=101; i<=200; i++){

//2->i-1

int j = 0;

for(j=2; j<=sqrt(i); j++){

if(i%j == 0){

break;

}

}

if(j>sqrt(i)){

count++;

printf("%d ", i);

}

}

printf("\ncount = %d\n", count);

return 0;

}

方法四:   继续对方法三优化,只要i不被[2, sqrt(i)]之间的任何数据整除,则i是素数,但是实际在操作时i不用从101逐渐递增到200,因为出了2和3之外,不会有两个连续相邻的数据同时为素数。

int main()

{

int i = 0;

int count = 0;

for(i=101; i<=200; i+=2){

//2->i-1

int j = 0;

for(j=2; j<=sqrt(i); j++){

if(i%j == 0){

break;

}

}

if(j>sqrt(i)){

count++;

printf("%d ", i);

}

}

printf("\ncount = %d\n", count);

return 0;

}

6.数9的个数,从1到100的所有整数中出现多少个数字9

思路为:   ①给一个循环从1遍历到100,拿到每个数据后进行一下操作   ②a. 通过%的方式取当前数据的个位,检测个位数据是否为9,如果是,给计数器加1    b. 通过/的方式取当前数据的十位,检测十位数据是否是9,如果是,给计数器加1 循环一直继续,直到所有的数据检测完,所有9的个数已经统计在count计数中。

#include

int main(){

int i = 0;;

int count = 0;

for (i = 1; i <= 100; i++){

if (i % 10 == 9)

count++;

if (i / 10 == 9)

count++;

}

printf("%d\n", count);

return 0;

}

7.分数求和,计算1/1 - 1/2 + 1/3 - 1/4 + 1/5 + … + 1/99 - 1/100的值,打印出结果

思路为:   ①从上述表达式可以分析出     a. 该表达式主要由100项,基数项为正,偶数项为负   ②设置一个循环从1~100,给出表达式中的每一项:1.0/i, 注意此处不能使用1,否则结果全部为0然后使用flag标记控制奇偶项,奇数项为正,偶数项为负然后将所有的项相加即可。

#include

int  main()

{

int i = 0;

double sum = 0.0;

int flag = 1;

for(i=1; i<=100; i++)

{

sum += flag*1.0/i;

flag = -flag;

}

printf("%lf\n", sum);

return 0;

}

8.求最大值,求10个整数中最大值

思路:

采用循环的方式输入一个数组使用max标记数组中的最大值,采用循环的方式依次获取数组中的每个元素,与max进行比较,如果arr[i]大于 max,更新max标记的最大值,数组遍历结束后,max中保存的即为数组中的最大值。

int main()

{

int arr[10] = {0};

int i = 0;

int max = 0;

for(i=0; i<10; i++)

{

scanf("%d", &arr[i]);

}

max = arr[0];

for (i = 1; i < 10; i++){

if(arr[i] > max)

max = arr[i];

}

printf("max = %d\n", max);

return 0;

9.乘法口诀表,输出9*9乘法口诀表

#include

int main(){

int i = 0;

for (i = 1; i <= 9; i++){

int j = 0;

for (j = 1; j <= i; j++){

printf("%d * %d = %d", i, j, i*j);

}

prrintf("\n")

}

return 0;

}

10.猜数字游戏

int ret = rand()%100:生成[0 - 100)之间的随机数   srand((unsigned)time(NULL)):保证每次电脑生成的随机数不一样。

void menu(){

printf("**********************\n");

printf("*******1.play******\n");

printf("*******0.exit*********\n");

printf("**********************\n");

}

void game(){

int randNume = rand() % 100 + 1;

while(1){

printf("请输入你要猜的数字:\n");

int num = 0;

scanf("%d", &num);

if (num < randNum){

printf("找小了\n");

}

else if (num > randNum){

printf("找大了\n");

}

else{

printf("找到了\n");

break;

}

}

}

int main(){

srand((unsigned)time(NULL));

int imput = 0;

do{

menu();

printf("请输入你的操作:1代表玩儿,0代表退出");

scanf("%d", &input);

switch (input){

case 1:

game();

break;

case 0:

printf("退出游戏\n");

default:

printf("输入有误\n");

break;

}

} while (input != 0)

}

11.二分查找,编写代码在一个整形的序列数组中查找具体的每个数,找到了打印出数字所在下标,找不到输出:找不到。

思路:

找到数组的中间位置检测中间位置的数据是否与要查找的数据key相等  a: 相等,找到,打印下标,跳出循环  b: key < arr[mid], 则key可能在arr[mid]的左半侧,继续到左半侧进行二分查找   c: key > arr[mid], 则key可能在arr[mid]的右半侧,继续到右半侧进行二分查找

如果找到返回下标,否则继续,直到区间中没有元素时,说明key不在集合中,打印找不到。

易错点:

right的右半侧区间取值,该值决定了后序的写法while循环的条件是否有等号求中间位置的方法,直接相加除2容易造成溢出更改left和right的边界时,不确定是否要+1和-1

// 方法一,采用[left, right] 区间

#include

int main()

{

int arr[] = {1,2,3,4,5,6,7,8,9,10};

int key = 3;

int left = 0;

int right = sizeof(arr)/sizeof(arr[0])-1; // right位置的数据可以取到

while(left<=right) // right位置有数据,必须要添加=号

{

int mid = left+(right-left)/2;    //可能造成数据溢出

if(arr[mid]>key) // key小于中间位置数据,说明key可能在左半侧,需要改变右边界

{

right = mid-1; // right位置的数据可以取到,因此right=mid-1

}

else if(arr[mid]

{

left = mid+1; // left位置的数据可以取到,因此left=mid+1

}

else

{

printf("找到了,下标是:%d\n", mid);

break;

}

}

if(left>right)

printf("找不到\n");

return 0;

}

// 方法二,采用[left, right) 区间

#include

int main()

{

int arr[] = {1,2,3,4,5,6,7,8,9,10};

int key = 3;

int left = 0;

int right = sizeof(arr)/sizeof(arr[0]); // right位置的数据取不到

while(left

{

int mid = left+(right-left)/2;    //这种方法不会溢出    //int min = letf + (right - left) / 2 这种算法可能造成溢出

if(arr[mid]>key) // key小于中间位置数据,说明key可能在左半侧,需要改变右边界

{

right = mid; // right位置的数据取不到,因此right=mid,不需要减1

}

else if(arr[mid]

{

left = mid+1; // left位置的数据可以取到,因此left=mid+1

}

else

{

printf("找到了,下标是:%d\n", mid);

break;

}

}

if(left>=right)

printf("找不到\n");

return 0;

}

二、关于函数与递归部分习题总结

1.关于函数的细节补充:

(1)关于函数返回值:   ①一个函数只能返回一个结果,可以考虑将两个数放在一个数组中,返回数组的地址   ②将形参存在数组中,修改数组中的内容,可以通过数组将修改结果带出去

//a为指针型变量,存放地址

//b为指针型变量,存放地址

void fun(int *a, int *b){   //fun(&c, &d) 传指针

*a = 10;  //解引用地址后,对地址里面的值进行赋值

*b = 20;

}

void fun(int a, int b){   //fun(c, d)  传值

a = 10;

b = 20;

}

③形参如果用指针,最终指向的是外部的实参,在函数中对指向内容进行修改,改变的就是外部的实参   ④全局变量不受函数的结束而结束,在函数中改变全局变量,主调函数中可以看到改变之后的结果。 (2)关于函数的调用:   ①形参按照值的方式传递,将来形参就是实参的一份临时拷贝,修改形参不会影响外部的实参。   ②函数可以传地址调用,传地址调用的时候,可以通过形参操作实参(此处拷贝的是一个地址,地址当中的值是通过解引用直接拿到的)。   ③函数可以嵌套调用,但不可以嵌套调用嵌套定义。   ④函数可以没有返回值,如果没有返回值也就不需要待会任何结果。   ⑤形参和实参在不同的函数中,即不同的作用域,因此形参和实参可以同名。   ⑥函数之间的数据传递可以使用全局变量。   ⑦函数的定义可以放在任意的文件中,使用时只需要包含头文件即可。 (3)关于递归的使用:   ①递归的两个条件:Ⅰ、将问题转化为子问题,子问题与原问题具有相同解法            Ⅱ、递归的出口   ②存在限制条件,当满足这个限制条件的时候,递归便不再继续。   ③每次递归调用之后,都是将原问题进一步缩小,就会越来越接近这个限制条件。   ④因为每次递归,相当于都是一次新的函数调用,而每次函数调用系统必须给该函数划分栈帧空间,内部的递 归函数没有退出,上层的递归就不能退出,栈帧就会累积许多块,如果累积超过栈的总大小,就会栈溢出。 (4)关于函数的声明和定义:   ①函数的定义可以放在任意位置,函数的声明必须放在函数的使用之前。(函数在使用之前可以先声明无定义)   ②函数定义在使用之后,使用之前没有声明时,编译器编译时识别不了该函数   ③函数的声明只是告诉编译器函数返回值类型、函数名字以及函数所需要的参数,函数定义才是说明函数是怎么 实现的 (5)关于实参和形参:   ①传参时不论是按照值还是指针方式传递,形参拿到的都是实参的一份拷贝。   ②形参是在函数调用的时候才实例化,才开辟内存空间(函数没有调用时,新参没有空间)。   ③如果是按照值的方式传递,形参和实参各自有各自的空间,改变形参不能改变外部的实参。 (5)关于函数的设计:   ①高内聚低耦合即:函数体内部实现修改了,尽量不要对外部产生影响,否则:代码不方便维护。   ②要尽可能不使用全局变量,全局变量每个方法都可以访问,很难保证数据的正确性和安全性。   ③参数越少越好,否则用户在使用时体验不是很好,还得必须将所有参数完全搞明白才可以使用。   ④设计函数时,尽量做到谁申请的资源就由谁来释放,否则如果交给外部使用者释放,外部使用者可能不知道或者忘记,就会造成资源泄漏。 (6)关于C语言函数的描述:   ①可以没有参数和返回值类型,根据需要给出。   ②函数的实参可能是变量,也可能是常量,也可能是宏,也可能是指针等等   ③在使用库函数时,必须要包含该库函数所在的头文件,否则编译器将不能识别   ④库函数是语言设计者为了让用户能够更好的使用,而给出的,但并不能解决用户的所有问题,因此其他问题还需要用户自己写方法解决

2.函数判断素数

#include

#include

int isPirme(int n){

int i = 2;

for (i = 2; i <= sqrt(n); i++){

if (n % i == 0){

return 0;

}

}

if (i > sqrt(n)){

return 1;

}

return 0;

}

int main(){

if (isPirme(30) == 0){

printf("不是素数\n");

}

else{

printf("是素数\n");

}

}

3.函数判断闰年

#include

int isLeapYear(int n){

if (n % 4 == 0 && n % 100 != 0 || n % 400 == 0){

return 1;

}

return 0;

}

int main(){

if (isLeapYear(2020) == 1){

printf("是闰年\n");

}

else{

printf("%不是闰年\n");

}

}

4.交换两个整数,实现一个函数来交换两个整数的内容

思路: 题目比较简单,此处只需要清楚传值和传指针的区别即可。   传值:形参是实参的一份拷贝,函数运行起来后,形参是形参,实参是实参,形参和实参没有任何关联性,改变形 参时,不会对实参造成任何影响。   传地址:形参是实参地址的一份拷贝,形参指向的实体是实参,对形参解引用后,拿到的内容就是实参,因此对形参解引用之后的内容进行修改,改变的就是实参.

#include

void Exchange(int *pa, int *pb)

{

int tmp = 0;

tmp = *pa;

*pa = *pb;

*pb = tmp;

}

int main(){

int a = 50;

int b = 100;

Exchange(&a, &b);

printf("交换后:%d %d\n", a, b);

}

5.乘法口诀,写一个函数,打印乘法口诀表,口诀表的行数和列数自己制定

#include

void mult(int a){

int i = 1;

for (i = 1; i <= a; i++){

int j = 1;

for (j = 1; j <= i; j++){

int mul = i * j;

printf("%d * %d = %d ", i, j, mul);

}

printf("\n");

}

}

int main(){

mult(9);

}

6.打印出一个数组的每一位,以递归的方式

#include

void printNumber(int n){

if (n < 10){

printf("%d ", n % 10);

}

else{

printNumber(n / 10);

printf("%d ", n % 10);

}

}

int main(){

printNumber(521);

}

7.用递归和非递归的分别实现求n的阶乘(不考虑溢出问题)

#include

//递归方法

int FacD(int n){

if (n == 1 ){

return 1;

}

int ret = n * FacD(n - 1);

return ret;

}

//循环方法

int FacX(int n)

{

int ret = 1;

for (int i = 1; i <= n; i++)

{

ret *= i;

}

return ret;

}

int main(){

printf("%d\n", FacD(5));

printf("%d\n", FacX(5));

}

8.用递归和非递归的方法分别实现strlen函数

#include

int MyStrlenD(const char*str){

if (*str == '\0'){

return 0;

}

else{

return 1 + MyStrlen(str + 1);

}

}

int MyStrlenX2(char str[]){

int size = 0;

for (int i = 0; str[i] != '\0'; i++){

size++;

}

return size;

}

int main(){

char *p = "abcde";

printf("%d\n", MyStrlenD(p));

printf("%d\n", MyStrlenX(p));

}

9.用递归和非递归的方法分别实现字符串逆序

思路: ①循环的方式

给两个指针,left放在字符串左侧,right放在最后一个有效字符位置交换两个指针位置上的字符left指针往后走,right指针往前走,只要两个指针没有相遇,继续2,两个指针相遇后,逆置结束

②递归方式: 对于字符串“abcdefg”,递归实现的大概原理:

交换a和g,以递归的方式逆置源字符串的剩余部分,剩余部分可以看成一个有效的字符串,再以类似的方式逆置

/*******递归的方式写字符串长度函数*******/

int MyStrlen(char *str){

if (*str == '\0'){  //当传入的字符串中没有字符

return 0;       //字符串长度为0

}

else{

return 1 + MyStrlen(str + 1); //运用递归,每递归一次长度加1,直到遍历到的'\0'时结束递归

}

}

/*******递归方式写字符串反转*******/

void reverse_string(char *string){

int len = MyStrlen(string);  //调用上面的字符串长度函数

if (len <= 1){               //当字符串长度小于等于1时,不执行

return 0;

}

else{

char temp = string[0];       //当字符串长度小于等于1时,不执行

string[0] = string[len - 1]; //将最后一个字符赋给第一个字符;

string[len - 1] = '\0';      //将最后一个字符的内容赋为'\0';

reverse_string(string + 1);  //递归调用下一次反转;

string[len - 1] = temp;      //将temp赋给当前的最后一个字符

}

}

void reverse_stringX(char *arr){

char *left = arr;

char *right = arr + MyStrlen(arr) - 1;

while (left < right){

char tmp = *left;

*left = *right;

*right = tmp;

left++;

right--;

}

}

int main(){

char ch[] = "abcdefgh";

reverse_string(ch);

printf("%s\n", ch);

reverse_stringX(ch);

printf("%s\n", ch);

return 0;

}

10.计算一个数的每位之和

#include

int DigitSun(int x){

if (x > 9){

return x % 10 + DigitSun(x / 10);

}

else{

return x;

}

}

int main(){

int num = 123456;

printf("%d\n", DigitSun(num));

return 0;

}

11.递归实现n的k次方

#include

int MyPow(int n, int k){

if (k > 0){

return MyPow(n, k - 1)*n;

}

return 1;

}

int main(void)

{

int ret = MyPow(5, 5);

printf("%d\n", ret);

return 0;

}

12.计算斐波那契数

//递归

int fibD(int n){

if (n <= 2)

return 1;

else

return fibD(n - 1) + fibD(n - 2);

}

//循环

int fibX(int n)

{

if (n <= 0){

return -1;

}

int f1 = 1;

int f2 = 1;

int f3 = 1;

for (int i = 3; i <= n; i++){

f3 = f1 + f2;

f1 = f2;

f2 = f3;

}

return f3;

}

int main(){

printf("%d\n", fibD(30));

printf("%d\n", fibX(30));

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值