爬楼梯问题
例、一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级台阶总共有多少种跳法(先后次序不同算不同的结果)。
示例:
输入0输出0
输入1输出1
输入10输出89
代码:
#include<stdio.h>
int main()
{
long f(int n); //声明计算函数
int n; //n表示青蛙跳的台阶数
long count = 0;
printf("请输入青蛙的台阶数:"); //输入跳跃台阶数
scanf_s("%d", &n);
if (n < 0) //输入数据小于0提示并退出程序
{
printf("输入数据错误\n");
return 0;
}
else
count = f(n); //计算方法数
printf("青蛙跳%d节台阶总共有%ld种跳法\n", n, count);
return 0;
}
long f(int n) //定义计算函数,f(n)表示跳n节台阶有f(n)种方法数
{
long f(int n);
long sn; //sn表示方法数
if (n == 0) //跳0节台阶有0种方法
sn = 0;
else if (n == 1) //跳1节台阶有1种方法
sn = 1;
else if (n == 2) //跳2节台阶有(1,1和2)两种方法
sn = 2;
else
sn = f(n - 1) + f(n - 2); //n大于2的方法数等于跳n-1节和跳n-2节方法数值和
return sn;
}
运行结果示例:
思路:第一眼看题目,看起来题目很复杂,要求每一次跳是跳一级还是跳两级,跳到最后要跳到n节台阶,这就很多情况,那我们如何才能考虑到这么多情况呢?这是我们就要将复杂问题分解成众多我们能解决的简单问题,那问题是我们又如何找到这些众多简单能解决问题是什么呢?很简单,我们一开始无法一下分解成众多可以解决问题,那就一点一点的分离,对于这个复杂的问题想我们是不是可以分解成另一个复杂的问题和一个或几个我们现在能解决的问题;然后又对于分解出来的另一个复杂问题再分解成复杂问题和现在能解决的问题,如此反复分解最终一个大的复杂问题就分解成了众多能解决的小问题。对于本题,我们要求跳到n节台阶的方法总数,想青蛙它一次只能跳一节或两节,青蛙能跳到第n节那它有可能是从第n-1节跳一级上来的也有可能是从第n-2节跳两级上来的,那求跳n节台阶方法数就等于跳n-1节和跳n-2节台阶方法数之和,问题就变成了分别求跳n-1节和跳n-2节方法数。求跳n-1节和跳n-2节方法数又和求跳n节思路一样。如此在对他们反复分解的话,最终所有问题变成了求跳1节和跳2节方法数,这两个我们就可以求出来分别是1,2。这样分解一个复杂问题就分解成了众多可以解决的小问题。
公式: f(n)=f(n-1)+f(n-2) [f(n)表示跳n节台阶方法数]
方法1:利用斐波拉契数原理求。斐波拉契数,第一和第二个数已知,从第三个数起该数是其前面两个数之和。所有跳n节台阶数组成的数组就是一个斐波拉契数列。可以用其性质求。
方法2:利用函数的递归调用。所谓函数递归调用就是主函数调用一个函数,而这个函数执行时又会调用自身函数。递归分为两个过程,先回溯,再递推。
理解函数递归调用:
我认为函数递归调用就像一个“包工头”。对于“包工头”面对一件事,他想的不是如何自己做完这件事,他是找了另一个人来完成大部分事情,而自己就做其中一小点事。
而恰恰他找的另一个人又是一个“包工头”,他又找了一个人来做大部分事情,他也只做其中一小点事。如此反复找下去就找到了最底层的“工人”。这就是回溯。
找到了“工人”就要开始干活,“工人”干完他要完成的事情“包工头”干他对应的事,直到整件事情完成。这就是递推。
算法流程:
1. 输入数据并判断数据是否错误
2. 计算跳n节方法数f(n)=f(n-1)+f(n-2)
f(n-1)=f(n-2)+f(n-3)
`````````````````
f(1)=1
f(2)=2
3.输出结果
进制转换数问题
例、做一个进制转换,输入一个int范围的十进制数,输入一个你想转换的进制(2~6)进制。输出你的计算结果。
实例:
输入 243 2 输出 11110011(输出一个十进制的数243,对应转换的为2进制,输出结果11110011)
输入 243 16 输出结果为F3
输入 243 8 输出结果为363
示例代码:
#include<stdio.h>
#include<string.h>
int main()
{
void change(long num, int n, char* str);//声明转换函数
void inversion(char* str); //声明字符串倒置函数
int n,num; //n表示要转换为的进制数,num表示要转换的数
char str[20];
printf("请输入一个十进制的数:");
scanf_s("%d", &num);
if (num < 0)
{
printf("输入数据错误"); //num小于0提示错误并结束程序
return 0;
}
else
{
printf("请输入转换为n进制数:");
scanf_s("%d", &n);
if (n < 2 || n>16)
{
printf("输入数据错误"); //n错误提示错误并结束程序
return 0;
}
else
{
change(num, n, str); //得到反序的n进制数字符串
inversion(str); //将反序的n进制数字符串倒置
printf("%d转换为%d进制数为%s\n", num, n, str); //输出n进制数字符串
return 0;
}
}
}
void change(long num, int n, char* str)//定义真正转换函数
{
int i = 0; //i表示下标
int sum, m; //sum,m存放中间数据
sum = num;
while (sum > 0)
{
m = sum % n;
if (m <= 9)
str[i] = 48 + m;
else //对于转换大于9进制数表示
str[i] = 87 + m;
i++;
sum = sum / n;
}
str[i] = '\0';
}
void inversion(char* str) //定义字符串倒置函数
{
int i, n;
char ch;
n = strlen(str);
for (i = 0; i < n / 2; i++)
{
ch = str[i];
str[i] = str[n - 1 - i];
str[n - 1 - i] = ch;
}
}
运行结果:
思路:进制转换这一类问题,首先你要知道一种如何将一种进制数转换为另一进制数。这里我介绍的是一种十进制数转换为其他各类进制数的方法。比如要将一个十进制数num转换为n进制数,先将计算m=num%n,此时m就是转换为n进制数的第一个数,然后将num除以n再赋给num;如果num大于0则继续计算m=num%n,此时m又是转换为n进制数的第二个数,然后再将num除以n赋值给num,又判断是否大于0,大于则又反复一样的操作,直到num小于0结束。对于记录结果有多种方式,可以用整形数组,也可以用字符数组。但这两种方式都要注意:第一先后得到的m依次存放在m[0],m[1]·······中,但是m[0]是输出结果的第一个数,m[1]是输出结果的第二个数··········所以要么有一个将存放结果数组倒置的过程要么输出是反序输出。第二对于要转换为大于9进制数时,m大于9时要输出或储存时转换为相应的a,b,c······
算法流程:
1.输入数据并判断数据是否正确
2.计算转换结果,num%n得到每一个转换结果数,并存放
3.将转换结果字符串倒置得到真正转换结果字符串
4.输出结果
顺时针打印二维数组
例、给定一个包含m x n个元素的矩阵(m行,n列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例1:
输入:
[ 1,2,3]
[4,5,6]
[7,8,9]
输出:[1,2,3,6,9,8,7,4,5]
代码:
#include<stdio.h>
#define N 10 //字符常量N表示可输出的最大限数
void PrintMatrix(int* num, int col, int low, int start) //定义输出第start圈
{
int Y_end = col - start - 1; //计算行最后一个位置的坐标
int X_end = low - start - 1; //计算列最后一个位置的坐标
int i = 0;
for (i = start; i <= X_end; i++)
{
printf("%d ", *(num + N * start + i)); //从左到右打印行
}
if (Y_end > start)
{
//由于从左到右输出完整的一行,故从上到下输出时,从下一行最后位置开始输出
for (i = start + 1; i <= Y_end; i++)
{
printf("%d ", *(num + N * i + X_end)); //从上到下打印列
}
}
if ( Y_end > start && start < X_end )
{
//由于从上到下输出完整的一列,故从右向左打印时,从前一列最后位置开始输出
for (i = X_end - 1; i >= start; i--)
{
printf("%d ", *(num + N * Y_end + i)); //从右向左打印行
}
}
//如果从下向上输出时,下一行和上一行的下标差为1,说明这一圈输出完成,不进行从下到上的打印
if ( X_end >start && Y_end-1 > start )
{
for (i = Y_end - 1; i >= start + 1; i--)
{
printf("%d ", *(num + N * i + start)); //从下到上打印列
}
}
}
void Print_Matrix(int* num, int col, int low)
{
if (num == NULL || col <= 0 || low <= 0)
{
return;
}
printf("数组顺序针输出结果是:");
int start = 0;
while (col > 2 * start && low > 2 * start) //圈数循环
{
PrintMatrix(num, col, low, start); //输出第start圈
start++;
}
printf("\n");
}
int main()
{
int num[N][N];
int m, n; //m表示行数,n表示列数
int* p = *num;;
printf("请输入数组行数m,列数n:");
scanf_s("%d%d", &m, &n);
if (m <= 0 || m > 10 || n <= 0 || n > 10) //输入数据错误提示并退出程序
{
printf("输入数据错误");
return 0;
}
else
{
printf("请输入数据:\n");
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
scanf_s("%d", &num[i][j]);
printf("原数组为:\n");
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("%5d", num[i][j]);
printf("\n");
}
Print_Matrix(*num, m, n); //输出结果
return 0;
}
}
运行示例:
思路:对于本题,可以随机画出一个矩阵数组,然后按照顺时针打印输出,就可以发现它整个过程就是一圈一圈的输出矩阵数组。每一次都是打印输出矩阵的最外圈,此时打印的矩阵数组变小,下一次打印输出变小矩阵数组的最外圈,依次打印输出矩阵最外圈直到输出所有矩阵数组元素。那问题就变成了,第一,如何顺时针输出一圈,第二,输出完一圈输出下一圈之间有什么联系,第三何时才是结束输出。第一顺时针输出一圈,其实就是先从左到右输出第一行,然后从上到下输出最后一列,再从右到左输出最后一行,最后从下到上输出第一列。第二输出完一圈输出下一圈之间的联系,其实你结合下标可以发现输出里层一圈的起始元素是输出外面一圈的起始元素的右下元素,那么开始时输出起始坐标为[start,start],则下一圈起始元素坐标为[start-1,start-1],如此的循环。第三何时结束输出循环,可以发现但输出圈数都大于整个数组行数和列数一半时就结束。
算法流程:
1. 输入数据
2. 顺时针输出一圈
(1)先从左到右输出第一行
(2)从上到下输出最后一列
(3)右到左输出最后一行
(4)从下到上输出第一列
输出下一圈,直到循环结束
3.输出结果
题外小分享:其实我看到的这题第一想到的是用递归函数来做。因为我认为递归函数它还可以达到一个循环的作用,如执行完依次操作在调用自己又进行一次操作,直到达到条件。但是我们又循环结构干嘛还要用什么递归函数来做,还很复杂难理解。当然我在这只是分享我自己好玩的想法,毕竟“包公头”才是递归函数最大的作用。