C言語の基礎知識 - 関数再帰

こんにちは、またお会いしました。前回の関数に続き、ライブラリ関数、カスタム関数、実引数、仮引数などについてお話しましたので、今日は関数再帰についてお話しましょう。関数再帰の特徴は、Big を置き換えることです。 、説明を始めましょう

関数の再帰

  • 再帰とは何ですか

    プログラムが自分自身を呼び出すプログラミング手法は再帰と呼ばれます。
    再帰は、プログラミング言語のアルゴリズムとして広く使用されています。プロセスまたは関数には、その定義または説明で直接的または間接的に自分自身を呼び出す
    メソッドがあります。通常、大規模で複雑な問題を層ごとに、元の問題と同様の小規模な問題に変換して解決します。
    再帰的戦略
    は A のみです。少数のプログラムを使用して、問題解決プロセスで必要な複数の繰り返し計算を記述することができるため、プログラム内のコード量が大幅に削減されます。
    再帰についての主な考え方は次のとおりです: 大きなものを小さくする

  • 再帰に必要な 2 つの特性

制約があり、この制約が満たされると再帰は続行されません。
この制限は、再帰呼び出しのたびにどんどん近づいていきます。
再帰は簡単にデッド再帰になる可能性があります。以下は最も単純な再帰です。

#include<stdio.h>
int main()
{
    
    
	printf("haha\n");
	main();
	return 0;
}

スタック オーバーフロー プログラムが終了するまで印刷を続けてください (笑)

演習: 整数値 (符号なし) を取得し、その各ビットを順番に出力します。
例:
入力: 1234、出力 1 2 3 4

#include<stdio.h>
void print(int n)
{
    
    
	if (n>9)
	{
    
    
		print(n / 10);
	}
	printf("%d ", n%10);
}
int main()
{
    
    
	unsigned num = 0;//定义无符号整型,相当于正整数
	scanf("%d", &num);
	print(num);
	return 0;
}

実行結果を見るために「1234」と入力しました
ここに画像の説明を挿入
絵で説明してみましょう
ここに画像の説明を挿入
シーケンスは 1-2-3-4-5-6-7 です

説明してみましょう
1. print関数に入り判定、n=1234、条件成立、実行 print(n/10)
2再度print関数に入り、判定、n=123、条件成立、実行print(n/10 )
3再度 print 関数に入り判定、n=12、条件成立、実行 print(n/10)
4再度 print 関数に入り判定、n=1、条件成立満たされていない場合は、printf("%d",n %10) を実行します。
5コードは関数に遭遇しません。前の print 関数に戻り、printf("%d",n%10) を実行します。
6戻り値を続けます
。 7最後に main 関数の print 関数に戻り、最終的にコードが停止します
。 上記のコード全体で実行すると、新しい print 関数が見つかったら開始し、print 関数がなくなるまで戻る必要があります。条件 n が常に 9 より大きい場合、それは永遠に継続するため、最終結果はスタック オーバーフローになります。
再帰を記述するときは、再帰して戻り、再帰を終了できるように制限を設ける必要があります。

練習用の書き込み機能では、文字列の長さを調べるための一時変数を作成することはできません。

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char arr[] = "abcdef";
	int len = strlen(arr);
	printf("%d", len);
	return 0;
}

ここに画像の説明を挿入
最初に再帰を行わずに通常の関数のコードを書き、一時変数を作成しましょう

#include<stdio.h>
int my_strlen(char* arr)
{
    
    
	int count = 0;
	while (*arr != '\0')
	{
    
    
		arr++;
		count++;
	}
	return count;
}
int main()
{
    
    
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

ここに画像の説明を挿入

カウントにはcountを使用、一度は実行せずにアドレスを1ビット後方に移動、判定時に条件成立時にcount+1

コードで上記の効果を実現するには、再帰メソッドを記述することもできます。

#include<stdio.h>
int my_strlen(char* arr)//用指针接收地址
{
    
    
	if (*arr != '\0')
	{
    
    
		return 1 + my_strlen(arr + 1);
	}

}
int main()
{
    
    
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

ここに画像の説明を挿入
上記のプログラムを実行すると、strlen で得られた結果と同じ結果が得られ、コードが正しいことがわかります。それでは、これを分析してみましょう。
ここに画像の説明を挿入

画像の方が面倒ですが、実際の処理は非常に簡単です。よく見てみると、エディターの描画がクソ
のように感じられるはずです。アドレス '\0' の内容は、そうでない場合は続行します。下に進み、my_strlen(arr + 1); に遭遇します。関数2を再度入力して関数に入り、アドレスの内容が '\0' であるかどうかを判断します。そうでない場合は、引き続き下に進み、my_strlen(arr) に遭遇します。 + 1); 関数3を再度入力して関数に入り、アドレスの内容が '\0' であるかどうかを判断し、そうでない場合はさらに下に進み、my_strlen (arr + 1); 関数4を再度入力して関数に入りますアドレスの内容が '\0' であるかどうかを判断する関数、そうでない場合は、引き続き下に進み、my_strlen(arr + 1) に遭遇します; もう一度関数 5 を入力して関数入り、アドレスの内容が '\0' であるかどうかを判断します'\0' です、そうでない場合は、引き続き下に進み、my_strlen(arr + 1) に遭遇します; 関数6を再度入力して関数に入り、アドレスの内容が '\0 ' ' であるかどうかを判断します。そうでない場合は、引き続き下降し、my_strlen(arr + 1) に遭遇します。関数7を再度入力し、'\0' に遭遇します。条件は満たされません。前後に進み、前の関数に入り、下に実行すると、長さが開始されます。 1 8を加えて戻り、長さに 1 9を加えて戻り、長さに 1 10を加えて戻り、長さに 1 11を加えて戻り、長さに 1 12を加えます。











逆に進み、長さに 1 を追加すると、
最終的な長さは 6 になります。

  1. 再帰と反復

n の階乗を求める練習をする

//求n的阶乘
int jie_cheng(int a)
{
    
    
	if (a >= 1)
	{
    
    
		return jie_cheng(a - 1) * a;
	}
}
#include<stdio.h>
int main()
{
    
    
	int n=0;
	scanf("%d", &n);
	int x=jie_cheng(n);
	printf("%d", x);

}

これを見たとき、実際に関数に再帰を書いたとき、それが私たちの数式に少し似ているように見えたことに気づいた友人はいますか? 答えは「はい」です。つまり、関数関数を実装したいときは、次のように書き出すことができます。最初に関数式を記述してから、コードを記述します

次にフィボナッチ数列の学習ですが、
まずフィボナッチ数列とは何か、つまり、前の項目と次の項目を足したものが後の項目と等しい、ということを3番目から順に説明していきます 1 1 2 3 5
8 13 21 34 55

したがって、私たちの式は、n>3 の場合、n=1 n=2 の場合、F(1)=F(2)=1 の場合、F(n)=F(n-1)+F(n-2) と書くことができます。 コード
デモ

#include <stdio.h>
int fib(int n)
{
    
    
    if (n <= 2)
    {
    
    
        return 1;
    }
    else
        return fib(n - 1) + fib(n - 2);
}
int main()
{
    
    
    int n = 0;
    scanf("%d", &n);
    int ret = fib(n);
    printf("第%d个斐波那契数列是:%d", n, ret);
    return 0;
}

上記のコードでも効果は得られますが、まだ欠点があり、フィボナッチ数 50 を計算すると、コンパイラの計算に時間がかかります。コードに count を追加してカウントし、繰り返された計算の回数を確認することもできます。

#include <stdio.h>
int count = 0;
int fib(int n)
{
    
    
    if (n == 3)
    {
    
    
        count++;
    }
    if (n <= 2)
    {
    
    
        return 1;
    }
    else
        return fib(n - 1) + fib(n - 2);
}
int main()
{
    
    
    int n = 0;
    scanf("%d", &n);
    int ret = fib(n);
    printf("第%d个斐波那契数列是:%d", n, ret);
    return 0;
}

ここに画像の説明を挿入
102334155 回繰り返す必要があることがわかります。
明らかに、このようなコードは特に優れたものではありません。現時点では、反復法を使用して計算できます。

#include<stdio.h>
int fib(int n)
{
    
    
	int i = 0;
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2)
	{
    
    
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
	if (n == 1 || n == 2)
	{
    
    
		return 1;
	}
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("第%d个斐波那契数列是:%d", n, ret);
	return 0;
}

ヒント

  1. 多くの問題が再帰的形式で説明されているのは、単に非再帰的形式よりも明確であるためです。
  2. ただし、これらの問題の反復実装は、コードが若干読みにくくなりますが、多くの場合、再帰的実装よりも効率的です。
  3. 問題が複雑すぎて反復的に実装できない場合、再帰的実装の単純さによって、それがもたらす実行時のオーバーヘッドを補うことができます。

関数のレッスンは終了しました。後で引き続き共有します。全員が毎日コードを入力する必要があります。また次回お会いしましょう。ありがとうございました。

おすすめ

転載: blog.csdn.net/2301_76895050/article/details/131484197
おすすめ