【编程学习C篇4】阶乘之和&&二分查找_2023_04_29

     今天我依旧是沿用之前的套路,先简单介绍一下必会的C语言知识,之后着重介绍一下本期的编程题目以及涉及的编程思想。虽然学习编程不久,但我已经感受到积累编程思想的重要性,如果说C语言,数据结构等编程知识是一台机器的零部件,那么编程思想就是驱动这台机动起来的能源所在,所以让我们一起在各种实例中积累经验,愈战愈勇吧。

 

一.ASCII码表:

目前为止,表中共定义了128个字符。

 

在计算机中,所有数据在存储和运算时都要使用二进制表示,那么像a、b、c、A、B、C等这些符号存储时也需要使用二进制来表示,所以ASCII码表就统一规定了上述常用符号用哪些二进制来表示。

这里需要注意的一点就是对应的字符也可以用它对应的十进制表示,比如a对应97,就会有如下的效果:

与此相关的一道题是让我们写一个只能输入大小写英文字母的程序,我们就可以利用大小写英文字母对应的ASCII码值的范围作为if语句限制条件:

//判断是不是字母
int main()
{
    char input;
    while (scanf("%c", &input) != EOF)
    {
        if ((input >= 65 && input <= 90) || (input >= 97 && input <= 122))
        {
            printf("%c is an alphabet.\n", input);
            getchar();
        }
        else
        {
            printf("%c is not an alphabet.\n", input);
            getchar();
        }
    }

	return 0;
}

二.栈区,堆区,静态区

栈区:存放局部变量形式参数

临时作用的变量均在栈区,这些变量都是进入作用域创建,出了作用域销毁。

堆区:与动态内部内存分配有关

malloc、calloc、realloc、free等。

静态区:存放全局变量静态变量

静态区的变量创建好后,直到程序结束才销毁。

三.goto语句:

goto语句是转向语句的一种,另外三个是break、continue和return。

goto语句可以随意滥用,但我们平时要尽可能的少用,因为goto语句可以强制改变程序执行顺序,容易写出Bug,使得程序结构层次不清,其实哪怕是完全不用goto语句程序也完全可以写得下去。

goto语句使用起来也非常简单:

这样一个死循环打印hehe的程序就写完了。原理就是当程序执行到goto语句时会被告知回到被标记处接着执行,然后循环往复永远走不出这个圈子(当然start标签也可以写在goto的下面)。

当然,不要觉得goto语句能写出死循环就理所当然的觉得它是循环语句哦。

那么goto语句在哪些情况下适用呢?

1.goto语句常用于跳出深层嵌套的循环。

2.goto语句通常和if语句连用。

四.阶乘之和:

阶乘求和 1! + 2! + 3! +...+ n! = ?

要写出这个程序,我们要先求一个数的阶乘,再把它们加起来。只求阶乘很简单,循环语句走起:

ret 和 i 的初始值不能为0,毕竟0乘以任何数均为0。

写到这里我们就可以考虑一下求 1!+2!+3!+num! 的值了。我一开始的思路是求出每一项的阶乘,之后再把它们相加:

//求阶乘之和(用迭代的方法)
#include <stdio.h>

int main()
{
	int num = 0;
	int j = 0;
	int i = 0;
	int ret = 1;
	int sum = 0;
	int num2 = 0;
	scanf("%d", &num);
	num2 = num;

	for (j = 0; j < num2; j++)
	{
		for (i = 1; i <= num; i++)
		{
			ret *= i;
		}
		sum += ret;
		ret = 1;
		num--;
	}
	
	printf("%d\n", sum);

	return 0;
}

这里需要注意:每次循环ret都要重新赋值为1,否则加入输入的num为3,先算3的阶乘ret为6,然后ret没变为1,再下一次循环算2的阶乘时,ret就等于6*2*1=12了。除此之外,每次循环后num要减1,否则输入num为3时,相当于算了3遍3的阶乘并相加,结果也是不对的。最后num2是什么呢?num输入3时,第一个for循环目的是要将3!、2!、1!这三项相加,第二个for是为了算出3!、2!、1!的值并相加,所以如果不单独创建一个num2在num--时,也会影响到第一个for循环的循环次数,进而影响结果。

那么此程序能否优化呢?

我们可以看到,上面的程序在算阶乘之和时,每项都要从1乘到最后,有许多多余的步骤,比如5的阶乘求和乘了5次1,4次2,3次3,2次4和1次5,我们能否改变思路,将程序修改为一遍乘一边求和呢?具体表现就是:算完 1! 后就开始加到sum里,乘2算到了 2! 再加到sum里,乘3就算完了 3! 再加到sum里,这样可以省去许多不必要的步骤。

下面是改进后的程序:

#include <stdio.h>

int main()
{
	int num = 0;
	int i = 0;
	int ret = 1;
	int sum = 0;
	scanf("%d", &num);
	
	for (i = 1; i <= num; i++)
	{
		ret *= i;
		sum += ret;
	}
	
	printf("%d\n", sum);

	return 0;
}

改进后不仅代码数有所减少,代码逻辑也简单许多,所以有时候避免一些刻板的思路,我们会发现有许多捷径可走。初极狭,才通人复行数十步,豁然开朗。

五.二分查找:

在以个有序数组中,数组内元素从小打到排列,写一个程序,快速高效找出输入的一个数在此数组中能否找到,若能找到,输出该数的下标,若找不到,输出找不到。

我们首先能想到的思路就是从左往右,依次一个一个比对,这种方法也能写得出来,但效率明显不够高,所以我们用二分查找的方法去编写程序。

什么是二分查找?试想一下,我们从整个数组的正中间那个数入手进行比对,若输入的数小于那个中间数,则说明输入的数一定小于中间数右侧的所有数,一次判断就可以剔除整个数组一半的数字,之后再求出数组左侧所有数字的中间数进行比对,以此类推,效率指数级别的上升。

以下是我写的代码:

//二分查找
	int i = 0;
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int num = 0;
	int left = 0;
	int right = (sizeof(arr) / sizeof(arr[0])) - 1;
	int mid = 0;
	printf("请输入要查找的数:> ");
	scanf("%d", &num);

	do
	{
		mid = (left + right) / 2;
		if (num == arr[mid])
		{
			printf("找到了,该数字的下标为:> %d", mid);
			break;
		}
		else
		{
			if (num < arr[mid])
			{
				right = mid - 1;
			}
			else
			{
				left = mid + 1;
			}
		}
	} while (left <= right);
	
	if(left > right)
	{
		printf("找不到了");
	}
	
	return 0;
}

这里面只有一点需要说明,循环结束条件为什么是lesft大于right呢?我们假设输入一个很大的数,那么数组中就找不到这个数,此时left会一直赋值为mid+1,right不动,最终必然有某个时刻,left大于right,这个时候我们发现其实在left等于right依旧找不到时就已经找不到这个数了,所以把循环的限制条件设置为仅当left>=right时进入循环。

此方法我觉得算是编程小白必会的一种编程思想,以后我也会继续积累经验,多记录,多分享。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值