仅有一行,输出i到N再到i

本文介绍了一个使用递归函数实现的特殊数字打印程序。该程序能够按照特定格式打印出一系列数字,通过递归调用自身并利用逻辑运算符来完成任务,避免使用常见的循环结构和条件判断。

转自:http://www.chinaunix.net/jh/23/918458.html

函数原形已经给出:int p(int i, int N);
功能:调用该函数,打印如下格式的输出,例p(1, 7);
1
2
3
4
5
6
7
6
5
4
3
2
1
即每行一个数字。(注意:N只打印一次)
要求:
函数中唯一能够调用的函数就是printf。
只使用一条语句,如果你真的不能用一条语句,每增加一条语句扣1分。
不准使用如下的关键字:typedef, enum, do, while, for, switch, case, break, continue, goto,
until, if,   ..具体很多我也忘了,反正能用的不多。
不能使用逗号表达式和?:表达式。
标准:(总分10分)
1. 每多一条语句扣1分,即每多一个;就扣1分
2. 每使用一次if或?:扣2分
3. 每使用一次for,while, swith各扣4分
大体上就是如上的要求了。

 

解答:

int p( int i, int N ){

     return ( printf( "%d/n", i ) && N > 1 && p( i+1, N-1 ) && printf( "%d/n", i ) ) + 1;

}
<think> 我们使用C89标准编写程序,输出n行杨辉三角。 杨辉三角的特点: 第n行有n个数。 每行的第一个数和最后一个数都是1。 从第三行开始,对于第i行(i从0开始计数),第j个数(j从1到i)等于上一行的第j-1个数与第j个数之和。 我们可以使用二维数组来存储杨辉三角的每一行,但由于我们只需要输出,所以可以只使用一个一维数组来迭代更新每一行。 步骤: 1. 读入整数n(表示要输出的行数)。 2. 使用一个一维数组来存储当前行,数组大小至少为n(因为第n行有n个数)。 3. 初始化数组的第一个元素为1(第一行只有一个数)。 4. 循环输出n行: - 对于当前行(假设为第i行,0<=i<n),我们需要计算这一行的每个元素。 - 注意:每一行的第一个元素总是1,最后一个元素也是1。 - 中间元素:从后往前计算(避免覆盖前一个元素的值),每个元素等于它自身(即上一行的旧值)加上它前面的元素(上一行的前一个位置的值)。 但是,如果我们使用一维数组并希望从前往后计算,那么我们可以先保存旧值,但更常见的做法是从后往前更新,这样不会覆盖后面计算需要用到的值。 - 然而,我们也可以使用两个数组:一个存储上一行,一个计算当前行。但为了节省空间,我们可以用一个数组,从后往前更新。 另一种简单的方法:使用两个数组,交替使用。但题目没有要求空间复杂度,我们可以使用一个数组,并采用从后向前更新。 这里我们采用一个数组,并从后向前更新每一行(除了第一个元素): 例如:当前数组存储的是第i-1行(有i个元素),现在要计算第i行(有i+1个元素)。 我们首先在数组的第i个位置(索引为i)写入1(最后一个元素)。 然后从后向前(从索引i-1到1)更新:arr[j] = arr[j] + arr[j-1] (因为此时arr[j]还是上一行的值,而arr[j-1]已经被更新为当前行的前一个值?) 注意:如果我们从后向前更新,那么当我们计算arr[j]时,arr[j]还是上一行的第j个元素(因为这一行还没有被覆盖),而arr[j-1]也是上一行的第j-1个元素?这样是不对的。 实际上,更简单的方法是:新的一行,我们可以在数组的前面部分从后往前更新,但这样容易出错。 我们换一种思路:使用一个数组,并利用上一行的数据。我们可以在每一行中,从后向前计算(这样不会覆盖上一行还未使用的数据): 第i行有i+1个数(行号从0到n-1): 第0行:1 第1行:1,1 第2行:1,2,1 第3行:1,3,3,1 ... 我们用一个数组arr,大小为n(足够大),初始时arr[0]=1,然后从第1行开始(即i=1): 首先,我们在第i个位置设置1(即arr[i]=1),然后从i-1位置开始往前,直到位置1:arr[j] = arr[j] + arr[j-1] 注意:因为arr[j-1]在上一行中就是上一行的第j-1个数,而arr[j]在上一行中实际上不存在(或者说为0),但我们并没有存上一行的最后一个数后面的数。 所以这种方法需要调整。 实际上,更常见的做法是:每一行从后往前计算,这样就不会覆盖前面的值。具体如下: 假设当前数组arr中存储的是第i-1行的数据(有i个元素),那么第i行有i+1个元素。 我们首先在最后添加一个0(但我们数组大小是n,所以我们可以认为后面有0),然后从后往前更新: 从j=i开始(即最后一个位置)到j=1:arr[j] = arr[j] + arr[j-1] 但是注意,第i行的最后一个元素应该是1,我们这样计算: 初始时,我们在arr[i]位置(原来存储的是0)加上arr[i-1](上一行的最后一个元素)?这样不对。 另一种常见方法(不使用0): 我们让数组arr[0]始终为1(第一行的第一个元素)。 然后对于第i行(行号从0到n-1),我们从第i个元素开始倒回到第1个元素,执行: arr[j] = arr[j] + arr[j-1] 但是这样会覆盖吗?我们来看第2行(即i=2): 第0行:arr[0]=1 第1行:我们想要arr[0]=1, arr[1]=1。那么我们在i=1时,从j=1开始(倒序): arr[1] = arr[1] + arr[0] -> 但是arr[1]现在是多少?初始时arr[1]是未定义的值(可能是0?),所以我们需要初始化每一行的最后一个位置为0?不对。 我们可以这样:在每一行开始前,我们在数组的当前行的最后一个位置后面添加一个0(但实际上我们并不需要的添加,因为我们数组足够大,可以认为后面有0)。然后从后往前更新: for j from i downto 1: arr[j] = arr[j] + arr[j-1] 然后我们设置arr[0]=1?不对,因为第一行我们只设置arr[0]=1,那么第二行: 我们首先在数组的第二个位置(即arr[1])我们认为是0(实际上可能是任意值),所以我们需要在每一行的开头,将当前行的最后一个位置(即第i行,索引为i的位置)设置为0(或者初始化为0),然后再开始更新。 具体步骤(用一个数组): 1. 定义数组arr,大小为n,初始化为0。 2. 将arr[0]设置为1。 3. 对于第i行(从0到n-1): a. 首先,我们设置一个临时变量?或者直接操作:将当前行的最后一个元素的下一个位置(即arr[i+1])我们并不需要,但我们需要从第i个位置开始倒着更新?不,我们只需要更新到第0个位置?不对。 b. 实际上,我们可以在每一行输出前,先输出当前行的数组,然后更新下一行。 4. 我们改变思路:先输出,再更新。 第0行:arr[0]=1,输出1,然后更新下一行(第一行): 为了更新第一行,我们从后往前更新:第一行有两个数,我们让arr[1]=0(然后更新:arr[1]=arr[1]+arr[0] -> 0+1=1,这样arr[1]=1),然后arr[0]怎么更新?我们不需要更新arr[0],因为第一行的第一个数还是1?但是这样arr[0]还是1,然后我们输出一行:arr[0]和arr[1](1和1)。 然后更新第二行: 先让arr[2]=0(因为当前数组只有前两个元素,第三个元素是未定义的,我们初始化为0),然后从后往前更新: arr[2] = arr[2] + arr[1] -> 0+1=1 arr[1] = arr[1] + arr[0] -> 1+1=2 arr[0]不变(还是1)? 这样第二行就是1,2,1。 所以算法: arr[0] = 1 对于第0行,我们输出arr[0] 然后对于第i行(i从1到n-1): 将arr[i]设为0(因为上一行结束时,arr[i]可能没有值,所以设为0) 然后从j=i开始,递减直到j=1:arr[j] = arr[j] + arr[j-1] 注意:这样更新后,arr[0]保持不变(总是1),而arr[i]被更新为1(因为最后一次更新:当j=1时,arr[1]被更新为arr[1]+arr[0],然后j=0我们不管,而arr[i]在更新过程中实际上被加了多次?不对,我们是从i开始,然后j=i时:arr[i] = arr[i](0) + arr[i-1](即上一行的最后一个元素,但此时arr[i-1]已经被更新了吗?) 我们重新考虑:我们是从j=i开始,递减到1。那么: j=i: arr[i] = arr[i] + arr[i-1] -> 0 + arr[i-1](上一行第i-1个位置的值,在上一行中,第i-1个位置的值是多少?实际上,在上一行中,我们只更新到第i-1个位置(即上一行只有i个元素,所以第i-1个位置是最后一个元素,值为1)?所以arr[i] = 1。 j=i-1: arr[i-1] = arr[i-1] + arr[i-2] -> 上一行中arr[i-1](即上一行的最后一个元素)和上一行的倒数第二个元素相加?不对,因为我们在更新上一行的时候,上一行已经改变了。 所以这个算法需要确保:我们更新当前行时,上一行的数据还在数组中(没有被覆盖),但这里我们覆盖了。 因此,我们采用另一种方法:用一个数组,但更新方向从后向前,并且不提前将arr[i]设为0。实际上,我们可以这样: 第0行:arr[0]=1 第1行:先输出第0行,然后更新到第1行: 在更新第1行时,我们想要两个数:1,1 我们可以:将arr[1]设为1(这样第0行最后一个位置后面我们加了一个1?)然后从后向前更新?或者直接设置: 我们可以:arr[1] = 1(因为上一行没有arr[1],所以直接设置),然后arr[0]不变。 第2行:我们想要1,2,1 我们先将arr[2]设为1(这样上一行最后一个位置后面加了一个1?),然后更新中间: arr[1] = arr[1] + arr[0] -> 1+1=2 然后arr[0]不变,arr[2]=1。 所以算法: 第i行(i从0开始): 设置arr[i] = 1 然后从j=i-1开始递减到1:arr[j] = arr[j] + arr[j-1] 这样,我们更新了当前行(第i行)的中间部分(除了第一个和最后一个,第一个我们没动,但第一个实际上在上一轮更新后已经改变了吗?) 我们来模拟: 第0行:i=0 设置arr[0]=1,然后j从-1到1(不执行循环),输出:1 第1行:i=1 设置arr[1]=1(现在数组:arr[0]=1, arr[1]=1) 然后j从i-1=0递减到1:j=0,循环条件j>=1不满足,所以不执行。输出:1,1 第2行:i=2 设置arr[2]=1(数组:arr[0]=1, arr[1]=1, arr[2]=1) 然后j=i-1=1,从1递减到1:执行一次:arr[1] = arr[1] + arr[0] -> 1+1=2,所以数组:arr[0]=1, arr[1]=2, arr[2]=1 输出:1,2,1 第3行:i=3 设置arr[3]=1(数组:1,2,1,1) j从2递减到1:首先j=2:arr[2]=arr[2]+arr[1] -> 1+2=3;然后j=1:arr[1]=arr[1]+arr[0] -> 2+1=3;然后j=0(停止) 数组:arr[0]=1, arr[1]=3, arr[2]=3, arr[3]=1 输出:1,3,3,1 这样是正确的。 所以步骤: 1. 定义数组arr,大小为n(足够大,至少为n) 2. 初始化:arr[0]=1(但注意我们后面会重新设置) 3. 循环i从0到n-1: a. 设置arr[i]=1 b. 循环j从i-1递减到1(包括1):arr[j] = arr[j] + arr[j-1] c. 然后输出当前行(从0到i) 注意:在设置arr[i]=1之前,arr[i]可能已经有值(上一行我们并没有设置它,所以初始化为0?)所以我们需要在循环开始前将整个数组初始化为0吗?不需要,因为我们在第i行才设置arr[i]=1,而在第i行之前,arr[i]没有被使用。 但是,我们在第i行设置arr[i]=1,那么上一行中arr[i]是什么?我们并不关心,因为上一行我们只用到0到i-1。所以可以。 注意:数组需要初始化为0吗?因为我们在第i行设置arr[i]=1,然后更新arr[i-1]到arr[1](这些位置在上一行已经被设置过值,所以没问题)。但是,当我们第一次设置arr[i]时,如果arr[i]原本是垃圾值,那么设置成1就覆盖了。因此,我们只需要确保在更新过程中,我们只使用已经初始化的元素(即0到i-1)并且我们在设置arr[i]=1时,它原本的值并不重要。 所以程序步骤: 1. 读入整数n。 2. 创建一个大小为n的整型数组(如果n比较大,比如100,那么数组大小100就够,因为最多100行,每行最多100个元素,但我们只用一个数组,所以100个元素即可)。 3. 初始化:我们可以将数组全部初始化为0,也可以不初始化,但为了安全,最好初始化为0。 4. 循环i从0到n-1: a. 将arr[i]赋值为1。 b. 循环j从i-1递减到1,步长为-1: arr[j] = arr[j] + arr[j-1] c. 然后输出当前行:从0到i,输出每个arr[j](并加上空格分隔) d. 每行输出后换行。 注意:我们更新时,j从i-1递减到1(包括1),所以当i=0时,j从-1到1(循环条件:j>=1,所以不执行)。当i=1时,j从0到1(但要求j>=1,所以j=0不满足,不执行)。所以只有i>=2时,才会执行内层循环。 输出格式:每一行的数字之间用空格分隔,并且每行前面可以适当添加空格以形成三角形(但题目没有要求,所以直接输出数字和空格即可)。 由于是C89,变量声明必须在作用域开始处,所以我们在函数开头声明所有变量。 代码示例: </think> 以下是使用C89标准编写的程序,实现读入整数n并输出n行杨辉三角。程序使用一维数组迭代计算,空间复杂度为$O(n)$: ```c #include <stdio.h> int main() { int n, i, j; int arr[100] = {0}; /* 假设最大行数不超过100 */ /* 读入整数n */ printf("请输入行数n: "); scanf("%d", &n); /* 生成并输出杨辉三角 */ for (i = 0; i < n; i++) { arr[i] = 1; /* 当前行最后一个元素设为1 */ /* 从后往前更新中间元素 */ for (j = i - 1; j > 0; j--) { arr[j] += arr[j - 1]; /* 杨辉三角递推公式 */ } /* 输出当前行 */ for (j = 0; j <= i; j++) { printf("%d ", arr[j]); } printf("\n"); } return 0; } ``` ### 程序说明: 1. **算法原理**: - 杨辉三角第$n$行的第$k$个元素由组合数$C(n,k)$决定 - 使用递推公式:$a_j = a_j + a_{j-1}$($j$从后往前更新) - 每行首尾元素固定为1,中间元素由上一行相邻元素相加得到[^1] 2. **空间优化**: - 仅使用一维数组存储当前行 - 通过**从后往前更新**避免数据覆盖问题 - 空间复杂度$O(n)$,优于二维数组的$O(n^2)$ 3. **输出示例**(n=5时): ``` 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 ``` ### 注意事项: 1. 数组大小预设为100,如需更大行数可调整 2. C89要求变量声明在作用域开头 3. 输入验证:实际应用中应添加`n>0`的检查
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值