再帰と反復
1. 再帰とは何ですか?
再帰は問題を解決する方法であり、関数がそれ自体を呼び出すときに行われます。
例:
int main() {
printf("调用函数main");
main();
return 0;
}
上記のコードは再帰の形式を示すだけであり、問題を解決するものではありません。
main 関数が main 関数内で呼び出されると、コードは最終的にデッド再帰に陥り、main 関数が無限に呼び出され、最終的にはスタック オーバーフロー (オーバーフロー) につながります。 )
出力結果がスタックし、エラー スタックがオーバーフローします。
再帰的思考:
大規模で複雑な問題を、元の問題に似ているが解決する規模が小さいサブ問題に変換します。大きな問題が分割されるまで、再帰は終了します。それは、大きなものを小さなものに還元するという考え方です。
2. 再帰の制限
書式形式での再帰には次の 2 つの必要条件があります。
1. 再帰には制限があり、この制限が満たされると、再帰は継続できなくなり、停止します。
2. 再帰呼び出しが行われるたびに、この制限にどんどん近づいていきます。
以下に、再帰的な質問の例をいくつか示します。
3. 再帰的な例
3.1 n の階乗を求める
计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
n的阶乘的公式:n! = n ∗ (n − 1)!
1.如果n等于0,则直接返回1,因为0的阶乘定义为1
2.否则,将n与(n-1)的阶乘结果相乘,并返回这个乘积
3.直至fact(n)中的n被减至0,跳出递归
//求n的阶乘
//计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
int fact(int n) {
if (n = 0) {
//如果n等于0,则直接返回1,因为0的阶乘定义为1。
return 1;
}
else//否则,将n与(n-1)的阶乘结果相乘,并返回这个乘积
{
//这里就是把一个大的数拆成了一个小的数乘上一个递归函数
//直至fact(n)中的n被减至0,跳出递归
return n * fact(n - 1);
}
return 0;
}
int main() {
int n;
scanf("%d", &n);
printf("%d!= %d",n,fact(n));
return 0;
}
3.2 整数の各桁を順番に出力する
输⼊⼀个整数n,打印这个按照顺序打印整数的每⼀位
输⼊:1234 输出:1 2 3 4
输⼊:520 输出:5 2 0
n % 10 は最後の桁を取得します
n / 10 は最後の桁を削除します
void Print_Frist(int n) {
if (n > 9) {
//这里先递归再去打印最后一位
Print_Frist(n / 10);
}
//打印最后一位
printf("%d ", n % 10);
}
int main() {
int n;
scanf("%d", &n);
Print_Frist(n);
return 0;
}
間違った考え:
void Print_Frist(int n) {
if (n > 9) {
//如果这里把n除以10,那么此次函数中的打印最后一位就会出问题
n = n / 10;
Print_Frist(n);
}
//打印最后一位,这里会出问题
printf("%d ", n % 10);
}
理由:
たとえば、1234 と入力します。if ステートメントで n = n / 10 の場合、
n を Print_Frist(n) 関数のパラメータとして使用します (実際には問題ありません)。ただし、今回は関数 printf("%d ", n % 10); n in もそれに応じて変化します。元々は 4 でしたが、n = n / 10 の後に 3 になりました。
関数が再帰的に呼び出されるとき、関数はまだ完全には終了していません。つまり、Print_Frist( n) が呼び出されると、次の printf("%d ", n % 10) はまだ実行されていません。必要なのは、
ループ呼び出し関数 /10 のパラメーターを変更することだけであり、この関数の n は変更されません。
4. 再帰と反復
次の質問では、フィボナッチ数関数のint Fibonacci(int n)のnは、0 から始まる関数内の単なるパラメーターであり、パラメーターは 0 (最初の項) であることを強調しておきます。(結果を出力するときは+1となります)
F(0) = 0 //1番目の項目
F(1) = 1 //2番目の項目
4.1 n 番目のフィボナッチ数を求める (再帰は推奨されません)
斐波那契数列是一个经典的数列,在数学上以如下递归关系定义:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2)
换句话说,斐波那契数列的前两项是0和1,之后的每一项都是前两项的和。
//递归方法
int count = 0;//调用函数次数
int Fibonacci(int n) {
if (n <= 1) {
count++;
return n;
}
else{
count++;
return Fibonacci(n-1) + Fibonacci(n-2);
}
}
int main() {
int n;
scanf("%d", &n);
printf("第%d斐波那契数 = %d\n", n + 1, Fibonacci(n));
printf("共调用了函数 %d 次\n",count);
return 0;
}
不適切です。
確かにこの計算では正しい結果が得られますが、この関数は 4,000 万回近く呼び出されています。C
言語では関数を呼び出すたびに、値を保存するためにスタック領域にメモリ領域を確保する必要があります。関数呼び出し中にさまざまなローカル変数が保存されるこの空間は、ランタイム スタックまたは関数スタック フレームと呼ばれます。
上記のフィボナッチ数 return Fibonacci(n - 1) + Fibonacci(n - 2) のステップでは、戻り値に対して関数が再帰的に呼び出されますが、関数はまだ終了しておらず、スペースも解放されていないため、新しいメモリ空間は、再帰ごとに新しいスタック フレーム スペースを開きます。スタック フレーム スペースは、リターンが開始されるまでレイヤーごとに解放されます。
4.2 n 番目のフィボナッチ数を見つける (反復推奨)
反復とは、操作またはプロセスを繰り返し実行する方法です。プログラミングでは、反復は通常、コレクション (リスト、配列など) 内の各要素を処理するため、または特定の条件が満たされるまでコードのブロックを繰り返し実行するために使用されます。(名前が示すように、これは変数を別の新しい変数に置き換える更新反復です。)
//迭代
int count = 0;
int Fibonacci(int n) {
if (n <= 1) {
count++;
return n;
}
int a = 0; // 第 0 项
int b = 1; // 第 1 项
int fib = 0; // 第 n 项
for (int i = 2; i <= n; i++) {
fib = a + b;
//相当于都往后走了一位,因为这三个数是连续的
a = b;
b = fib;
count++;
}
return fib;
}
int main() {
int n;
scanf("%d", &n);
printf("第%d个斐波那契数 = %d\n",n+1, Fibonacci(n));
printf("共执行了 %d 次\n",count);
return 0;
}
上記のコードは、再帰効率よりもはるかに高い反復効率を使用しています。
4.3 概要
再帰は非常に良いアイデアでありアイデアですが、やみくもに再帰に取り組むことはできません。どの方法にもそれぞれ長所と短所があるため、特定の問題を詳細に分析する必要があります。問題には多くの解決策があり、効率が優先されます。 1つ。