[データ構造学習レコード7]-スタックと再帰(実際、より多くの分析と再帰)

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=0nF a c t n1 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 n1 F i b n2 n>>1

2.実現する

私たちは、定数項を考えること、と呼ばれている递归边界等の要因として、f(n)=1,n=0フィボナッチ数やフィボナッチ数列f(n)=0,n=0f(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に移動すると、大きなプレートを小さなプレートに配置できなくなります。

このために、さらに数回試行します。

  1. n = 1

    1セット目1A ----> C

  2. n = 2

    第1セット1A ----> B

    2セット目2A ----> C

    3セット目1B ----> C

  3. 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つの全体と見なすことができます。次に、私たちのプロセスは次のとおりです。

  1. 上記のAからBまでのすべてのプレート
  2. 底板はAからCになります
  3. 上記の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つのことを完了します。

  1. 呼び出された関数にすべての実際のパラメーター、戻りアドレス、およびその他の情報を渡して保存します。
  2. 呼び出された関数のローカル変数にストレージ領域を割り当てます
  3. 呼び出された関数のエントリに制御を移します。

次に、呼び出された関数が呼び出し元の関数に戻る前に、次の3つのことを完了する必要があります。

  1. 呼び出された関数の計算結果を保存します
  2. 呼び出された関数のデータ領域を解放します
  3. 呼び出された関数によって保存されたリターンアドレスに従って、呼び出し元の関数に制御を移します。

再帰的にネストされた呼び出しを行うとすると、最初に呼び出された関数は最後に戻る必要があるため、コンパイラー内では、これらの関係と情報がスタックを介して保存されます。

したがって、再帰間の情報転送ではスタックを使用する必要があります。幸い、コンパイラがスタックを管理递归工作栈するため、再帰の境界と再帰の内容を考慮するだけで済みます。

おすすめ

転載: blog.csdn.net/u011017694/article/details/109428873