@Stackと再帰(実際には、より解析的な再帰です)
1.再帰
1.コンセプト
再帰とは、高校の数学で学んだ再帰です。もちろん、漸化式が関数として表現されている場合は、この関数を呼び出すことができます递归函数
。递归函数
これは、再帰関数と呼ばれる、直接または一連のステートメントを介して自身を呼び出す関数です。
たとえば、階乗関数:
F act(n)= {1、n = 0 n・F act(n − 1)、n> 0 Fact(n)= \ left \ {\ begin {aligned} 1、n = 0 \\ n \ cdot Fact(n-1)、n> 0 \\ \ end {aligned} \ right。F a c t (n )={{
1 、n=0n⋅F a c t (n−1 )、n>>0
または、おなじみのフィボナッチ数列:
Fib(n)= {0、n = 0 1、n = 1 F ib(n − 1)・Fib(n − 2)、n> 1 Fib(n)= \ left \ {\ begin {aligned} 0、n = 0 \\ 1、n = 1 \\ Fib(n-1)\ cdot Fib(n-2)、n> 1 \\ \ end {aligned} \ right。F i b (n )=⎩⎪⎨⎪⎧0 、n=01 、n=1F i b (n−1 )⋅F i b (n−2 )、n>>1
2.実現する
私たちは、定数項を考えること、と呼ばれている递归边界
等の要因として、f(n)=1,n=0
フィボナッチ数やフィボナッチ数列f(n)=0,n=0
、f(n)=1,n=1
。
次に、フィボナッチ数列を実装する再帰コードを記述します。
int Fib(int n)
{
if (n == 0) return 0;
else if (n == 1) return 1;
else
{
return Fib(n-1) + Fib(n-2);
}
}
int main()
{
printf("%d", Fib(10));
return 0;
}
再帰関数式を記述できる限り、その再帰関数を記述できます。私の考えでは:再帰的な境界が最も重要です。再帰境界が適切に処理されない場合、再帰は永久に継続し、その後バーストして死ぬ可能性があります。
3.より難しい再帰分析-ハノイの塔
ハノイの塔でのゲームのルールは次のとおりです。AのプレートをプレートCに移動すると、大きなプレートを小さなプレートに配置できなくなります。
このために、さらに数回試行します。
-
n = 1
1セット目1A ----> C
-
n = 2
第1セット1A ----> B
2セット目2A ----> C
3セット目1B ----> C
-
n = 3
1セット目1A ----> C
第2ラウンド2号A ----> B
3セット目1C ----> B
4セット目3A ----> C
第5ラウンドNo.1B ----> A
6セット目B ----> C
7番目のディスク1A ----> C
全体論的アプローチを使用する場合、プレートの山をAからCに移動すると、Aピラー上のプレートは、下部プレートとその上のすべてのプレートの2つの全体と見なすことができます。次に、私たちのプロセスは次のとおりです。
- 上記のAからBまでのすべてのプレート
- 底板はAからCになります
- 上記のBからCまでのすべてのプレート
では、トッププレートをBとCに移動するにはどうすればよいでしょうか。プレートのこの部分を、下部プレートと上部のすべてのプレートの2つの部分と考えてください。
Bに移動すると、Cが補助タワー(ゲームがAからCに移動するため、Bが補助タワー)になるため、上部で「ハノイの塔AからBへの操作」を実行します。
逆に、BからCに移動すると、Aが補助タワーになるため、「BからCへの溶接ノッチ操作」を実行します。
人形を始めます。最後までプレートが1つしかないので、明らかに「ハノイの塔操作のAからC」を実行し、プレートが1つしかないので、それをCに移動します。
次に、再帰式が出てきます。
再帰境界: n=1时 把1号盘子 A→C
再帰内容:① 把n-1号盘子 A→B
② 把n号盘子 A→C
③把n-1号盘子 B→C
さて、コードを実装しましょう:
void hano(int n, char a, char b, char c) // hano(n=有几个碟子,a=主塔,b=辅助塔,c=目标塔)
{
if (n == 1) printf("No.%d %c -> %c\n", 1, a, c);
else
{
hano(n - 1, a, c, b); // 这里是A to B的操作,C是辅助塔
printf("No.%d %c -> %c\n", n, a, c);
hano(n - 1, b, a, c); // 这里是B to C的操作,A是辅助塔
}
}
int main()
{
hano(3, 'A', 'B', 'C'); // 这里是A to C的操作
return 0;
}
2.スタックと再帰の関係
高水準言語では、(これは動詞です)関数を呼び出す必要がある場合は、、、调用(名词)函数
および被调用函数
内部スタックを介したニーズ間の情報交換にリンクします。
呼び出された関数を実行する前に、システムは次の3つのことを完了します。
- 呼び出された関数にすべての実際のパラメーター、戻りアドレス、およびその他の情報を渡して保存します。
- 呼び出された関数のローカル変数にストレージ領域を割り当てます
- 呼び出された関数のエントリに制御を移します。
次に、呼び出された関数が呼び出し元の関数に戻る前に、次の3つのことを完了する必要があります。
- 呼び出された関数の計算結果を保存します
- 呼び出された関数のデータ領域を解放します
- 呼び出された関数によって保存されたリターンアドレスに従って、呼び出し元の関数に制御を移します。
再帰的にネストされた呼び出しを行うとすると、最初に呼び出された関数は最後に戻る必要があるため、コンパイラー内では、これらの関係と情報がスタックを介して保存されます。
したがって、再帰間の情報転送ではスタックを使用する必要があります。幸い、コンパイラがスタックを管理递归工作栈
するため、再帰の境界と再帰の内容を考慮するだけで済みます。