再帰的なベストプラクティス

年生の多くの人々は、それが再帰と接触していたが、私は確信して、初心者がちょうど再帰を使い始めるとき、無知に見えることを余儀なくされた多くの人々よ、私はあまりにも、私はそれを感じました、再帰素晴らしいです!

そこにも、ほとんどの人が知っている再帰的であってもよいが、再帰を理解するために見ることが、問題を行うための実際のプロセスではなく、混乱する再帰的であることに時々発生しやすい、使用する方法がわかりません。私は再帰ショートカットああを把握しなければならない場合、いくつかの人々が私に尋ねもあります。非常に多くのショートカットは、ああ、私はまだ記事、私の経験のいくつかの話を書きたい場合には、正直に言うと、多分、あなたにいくつかの助けをもたらすことができます。

アカウントに初心者を取るために、私は最も簡単な質問から始めます!

三つの要素再帰

最初の要素は:あなたがしたい、この機能をオフに

再帰のために、私は非常に重要なことは、ということだと思います。この関数の機能は何である彼は一つのことが何であるかを完了したいと考えていますが、これは完全にあなた次第に定義することです。つまり、我々は最初にどんなコード内で機能しませんが、最初にあなたは、この機能がやっている使用することを理解する必要があります。

例えば、私は、関数を定義します

1 // 算 n 的阶乘(假设n不为0)
2 int f(int n){
3
4 }

この機能のオペレータは、nの階乗です。まあ、我々は機能を定義している、と定義し、その機能が何であるか、我々は第二の要素を見てください。

第二の要素:再帰終了条件を見ます

いわゆる再帰、コード内のその意志機能、我々が見つけなければならないので、機能自体を呼び出す再帰条件の終わりを、そうでない場合は、底なしの穴に身を呼び出すことだったでしょう。言い換えれば、私たちが知る必要がある時に引数なぜ、再帰終了し、結果が返された直後に、この時点で我々は、このパラメータの値に基づくものでなければならないことに注意をすることができますしてください直接関数イエスのどんな結果を知っています。

例えば、上記の例では、n = 1のときは、あなたは直接F(n)は、それがどのようなものですが知ることができるでしょうか?この場合には、F(1)= 1。以下のように、私たちの内部関数のコード、追加第二の要素内のコードを改善

1 // 算 n 的阶乘(假设n不为0)
2 int f(int n){
3    if(n == 1){
4        return 1;
5    }
6  }

一部の人々は、n = 2の場合、言うかもしれないが、我々は再帰として= 2つの終了条件のn私は置くことができることを、ああ、に等しいどのくらいのF(n)はそれを知ることができますか?

次のコードも可能ですので、もちろん、限り、あなたはどう思うかのパラメータは、あなたが直接、関数の結果を知ることができるよう、あなたは、エンディングの条件として、その引数を使用することができます。

1 // 算 n 的阶乘(假设n>=2)
2  int f(int n){
3    if(n == 2){
4        return 2;
5    }
6  }

私は仮定して、コメント内のコードを書くN> = 2、n = 1の場合ため、かつn <= 2、F(n)が見落とされるであろう= Nので、より正確にするために、我々はこれを書くことができることに注意してください。

1 // 算 n 的阶乘(假设n不为0)
2 int f(int n){
3    if(n <= 2){
4        return n;
5    }
6  }

第三の要素:同値関係関数を見つけるために

第三の要素は、私たちがしたいことであるパラメータの範囲を狭めるために引き続き、狭小化した後、我々はいくつかの補助変数または操作を、そのまま元の関数の結果を渡すことができます。

例えば、Fこの範囲の(N)が比較的大きい場合、我々は、F(n)を作ることができる= N * F(N-1)。したがって、スコープとなるN N-1、狭い範囲、及び元の関数f(n)は変わらないため、我々は、F(n-1)のようにする必要がn倍されます。

端的に、F(n)は、同値関係本来の機能を見つけることである等価関係N * F(N-1)であり、すなわち、

F(N)= N * F(N-1)。

この同値関係が探している、それは言うことができる最も困難なステップであり、そしてあなたはかなりあなたは天才じゃないので、それが問題で、あなたはまた、いくつかの質問でより多くの接触を持っている必要はありません理解していない場合、私は10を見つけるために、次の記事になります道路再帰的な質問は、あなたが徐々に慣れること

私たちのコードを改善していき、この等価性を識別し、我々は中に同等の機能に書き込まれ、この式を置きます。次のとおりです。

1  // 算 n 的阶乘(假设n不为0)
2  int f(int n){ 
3    if(n <= 2){
4        return n;
5    }
6  // 把 f(n) 的等价操作写进去
7   return f(n-1) * n;
8 }

これまでのところ、再帰的な3つの要素が、すでに私たちが書いた内部コードF(n)が機能して、コードの中に書かれています。

これは、再帰の三つの要素の中で最も重要であり、あなたはこれらの三つの要素を見つけることを試みるために自分自身を強制するたびに再帰的。

まだ理解していませんか?私がこのモデルに基づいていくつかの問題について話しましょう、重要ではありません。

いくつかは、私があまりにもシンプルに書いた小さな基盤、参照するには無い忍耐を感じることがありますか?Shaoxiaは、私は以下についてお話します、見て続けてください再帰を最適化する方法もちろん、直接私にいくつかのアドバイス、非常に感謝を与えるメッセージの下部を引くことができ、首長へお気軽にどうぞ!

ケース1:フィボナッチ数

1,1,2,3,5,8,13,21,34 ...、即ち、第一項f(1)= 1、第二項f(2)=:フィボナッチ数は、列のような数です。 1 ......、N番目の項目は、F(N)= F(N-1)+ F(N-2)です。nの値を検索すると、アイテムの数です。

図1において、第1の再帰的機能 -

次のように仮定するF(n)は、最初のn個のアイテムの評価の関数です。

1 int f(int n){
2
3 }

2、再帰条件の終わりを見つけます

明らかに、n = 1の場合又はn = 2、我々は簡単に結果f(1)= F(2)= 1を知ることができます。したがって、終了条件は、再帰N <= 2であってもよいです。コードは以下の通りであります:

1 int f(int n){
2    if(n <= 2){
3        return 1;
4    }
5 }

第三の要素:同値関係関数を見つけるために

我々は簡単にF知ることができるので、タイトルは、私たちに同値関係を持っている(N)= F(N-1)+ F(N-2)。私は同値関係を見つけることが最も困難の一つであり、その対象は、私たちに関係が混乱し、これはあまりにも簡単である私の読者は、アカウントに、ほぼゼロベースを取るために、よく、これは、と述べました。

したがって、最終的なコードは次の通り:

1	 int f(int n){ 
2    // 1.先写递归结束条件
3    if(n <= 2){
4        return n;
5    }
6    // 2.接着写等价关系式
7    return f(n-1) + f(n - 2);
8 }

取得し、それは単純ではないのですか?

ゼロベースか理解できないことがあり、それが問題で、ゆっくりとこの練習モードに従っていません!まあ、Tucaoに単純すぎる兄があるかもしれません。

ケース2:暁馬跳び階段

カエルは、クラス1レベルにジャンプすることができ、あなたはまた、レベル2に飛び乗っすることができます。カエルはどのように多くのジャンプのn級レベルの合計を求めて飛び込みました。

図1において、第1の再帰的機能 -

次のように仮定するF(n)はカエルジャンプステップを求めてn段目の関数であり、種の総数をジャンプ。

1 int f(int n){ 
2
3 }

2、再帰条件の終わりを見つけます

私は、あなたは、nは小さく、我々はそれがそうするとき、N = 1という、直感的なF(n)がどれだけ簡単に計算しているため、あなたが直接nまで、ライン上で非常に小さなに圧縮、再帰終了条件を求めている、と述べましたそれがどのくらいであるF(1)を知っていますか?直感的に十分な、右?すなわち、F(1)= 1。コードは以下の通りであります:

1 int f(int n){
2    if(n == 1){
3        return 1;
4    }
5 }

第三の要素:同値関係関数を見つけるために

すべてのジャンプ、少しカエルは、より高いレベルにジャンプすることができ、あなたは2つのステップをジャンプすることができ、ある、あなたがジャンプするたびに、二つの小さなカエルのジャンプがあります。

最初のジャンプ方法:私はステップをジャンプ初めて、その後、ジャンプのn-1個の残りのステップのF(n-1)のタイプがあり、N-1レベルが跳ね上がっていない左。

第二ジャンプは:最初の2つのステップをジャンプし、残りのn-2 Geはまだステップ、残りのn-2のGeステップジャンプがf(N-2)を有する種。

したがって、少しカエルの全ては、両方の和、即ち、F(N)= F(N-1)+ F(N-2)がジャンプするジャンプ。これまでのところ、求めて上の同値関係。書き込みコードので:

1 int f(int n){
2    if(n == 1){ 
3        return 1;
4    }
5    ruturn f(n-1) + f(n-2);
6 }

私たちは、その上のコード、右を感じますか?

、ほとんどの場合にはn = 2、そこであろう明らかF(2)= F(1 )+ F(0)。我々はコールをダウン継続するが、上記のコード・ロジックは、我々は、F(0)を呼び出しし続ける、F(0)= 0は、再帰的な論理の終わりではなく、知っている= F(-1 )+ F(-2) これは、無制限の通話につながる入力することができます無限ループ

これは、私がしたいとあなたが言うことであるについて再帰終了条件が深刻な十分な問題であり、多くの人が再帰を使用することにありますが、終了条件が原因で無限ループが発生して、その結果、厳しい十分ではありません。私たちは第二段階、再帰終了条件に判明し、終了条件がコードに書き込まれ、その後、第三のステップが、可能な場合には、あるしてください。なお、第三の工程の後に、我々は同等の機能を見つけ、第三ステップ関数呼び出し関係に基づいて、第2工程を戻っていた、それが欠落しているいくつかの終了条件となります。上記、F(N-2)のように、この関数が呼び出され、(0)は、f場合があり、我々は上に戻ってそれを置くように、無限ループをもたらすと思われました。コードは以下の通りであります:

1	 int f(int n){
2    //f(0) = 0,f(1) = 1,等价于 n<=2时,f(n) = n。
3    if(n <= 2){
4        return n;
5    }
6    ruturn f(n-1) + f(n-2);
7 }

いくつかは、私は私の終了条件が行う方法を見逃していないかわからない、と言うかもしれませんか?心配しないで、さらにいくつかの練習を行う方法を知っています。

誰かがTucaoしたい場合がありますここを参照してください、これらの二つの質問は、あまりにも簡単に右ですか?あなたはそうおざなりにすることはできません。Shaoxiaは、ああ、ここでは少し難しいのデビューだ行っていません。

ここにも難しいことではありません、それは特に同等の発見の第三段階で、難しい少し上の話題を超えています。

ケース3:単独リンクリストを逆にします。

単独リンクリストを逆にします。例えば、リストである:1-> 2-> 3-> 4。反転後、4-> 3-> 2-> 1

次のように定義されたノードのリスト:

1 class Node{
2    int date;
3    Node next;
4 }

Java言語が、しかし、あなたは、Javaを学んだことがない場合でも、私はそれはほとんど効果、私は理解できていると思います。

それとも古いルーチン、ステップバイステップの三つの要素。

図1に示すように、再帰関数定義します

仮定する関数reverseList(ヘッド)は、リストの逆関数であるが、ヘッドノードがリストの先頭で表されることを特徴とします。コードは以下の通りであります:

1 Node reverseList(Node head){
2
3 }

終了条件を探す2

ときに、リストは一つのノードのみ、またはテーブルが空である場合、あなたはそれの結果を知っている必要がありますか?漢はまた、リターン聖歌に直接、直接、頭を乾燥させません。コードは以下の通りであります:

1 Node reverseList(Node head){
2    if(head == null || head.next == null){
3        return head;
4    }
5 }

同値関係3.ルック

この同値関係nは、値ではないとして、見つけるのは簡単です。しかし、私はあなたを教え、それは同等の条件をだ、範囲は、ノード数、リンクされたリストは、常に、より小さくなっている常にリストについては、縮小しなければなりませんので、あなたは、あなたの最初のreverseListの(それを見つけることができない場合head.next)を再帰的に結果がZeyangで見に再度行きます。例えばとしてリストノード

IMG

我々は、すなわち、コードは以下、2-> 3-> 4再帰最初の試行で絞り込ま

1 Node reverseList(Node head){
2    if(head == null || head.next == null){
3        return head;
4    }
5    // 我们先把递归的结果保存起来,先不返回,因为我们还不清楚这样递归是对还是错。,
6    Node newList = reverseList(head.next);
7 }

私達の時間の最初のステップでは、それは、一本鎖逆転の関数とすることができる機能をt reverseLisが定義されているので2-> 3->反転後4件の結果がそうであるべきです。

IMG

我々2-> 3-> 4再帰的に4-> 3-> 2。次のノード1はまだ2を接続するのでしかし、ノード1は、我々は、それに触れることはなかったです。

今何?方法は?

実際には、次のは、単純な、私たちはその後、唯一必要であるではないラインで、nullに、1に、次の1点を次のノード2点?、すなわち、結果newList以下のリストによる変更後:

IMG

すなわち、reverseList(ヘッド)と等価** reverseList(head.next)** + つのノード1及び2の変化点(詳細な説明)を以下のようにまあ、同値関係を見つける、コードは次のとおりです。

 1 //用递归的方法反转链表
 2 public static Node reverseList2(Node head){
 3    // 1.递归结束条件
 4    if (head == null || head.next == null) {
 5             return head;
 6         }
 7         // 递归反转 子链表
 8         Node newList = reverseList2(head.next);
 9         // 改变 1,2节点的指向。
10         // 通过 head.next获取节点2
11         Node t1  = head.next;
12         // 让 2 的 next 指向 2
13         t1.next = head;
14         // 1 的 next 指向 null.
15        head.next = null;
16        // 把调整之后的链表返回。
17        return newList;
18    }

この質問は非常に無知な第三のステップを見ているのですか?通常、あなたがあまりにも少ない、ので、またその上にさらにいくつかの練習を考えていないかもしれないので。しかし、私はこれらの3つの質問を通じて、私はこのモデルを考えるとあなたは再帰で問題が発生した後、あなたが質問に未来を行うことができないときのいくつかのアイデアを与えることを願っています。再帰的で、物品を把握することは不可能ですが、また、より多くの練習は、私はあなたが真剣に、私の記事を見て何回かそれを読んで、いくつかのアイデアを見つけることができると信じています!

私は彼らが学ぶために、私はおそらく戻って10回の再帰的な演習を考え探して、そう、さらにいくつかの練習を何度も強調してきたが、私は難しいかもしれません探しています。それが今日のようではないので、シンプルなので、初心者の私は、習得再帰、あなたの抽象的思考能力が強くなると信じて、自分で問題を見つけなければならないと思いました!

次に、私はいくつかの最適化再帰について話しています。

再帰に関するいくつかの最適化の考え方

繰り返し計算するかどうかを検討1.

最適化されていないときは、再帰を使用する場合、非常に非常に非常に多くそこにあるもの、あなたを教えてサブ問題は、二重カウントします。

子供の問題は何ですか?F(N-1)、F(N-2)...それは副問題のF(N)です。

例えば、ケース2その質問、F(N)= F(N-1)+ F(N-2)。図再帰呼び出しの状態を次のように

IMG

再帰的演算、計算が二回F(5)、5 F(4)を繰り返したとき、見られません。これは非常に怖いです、nは大きく、より多くのダブルカウントので、我々は、最適化されなければなりません。

どのように最適化するには?一般的に、我々は我々の計算の結果は、例えば、F(4)あなたは(4)fを再計算するときの計算結果が、それを保証するには、我々が最初に計算するかどうかの前に、何を決定、それを保証する置くことができ、直接に計算した場合にはF(4)結果を取り出すことができ、それは次に、再帰的計算を算出していませんでした。

何がそれを保存しますか?アレイを保存するために使用されるか、またはnが我々のアレイ、F値として(n)は、例えばARRを添字としてHashMapのは、我々は、保持するためにアレイを使用することができる[N] = F(N)。F(n)が時間的に算出されていない、我々は、[n]は、特定の値、例えばARR [N] = -1に等しいARRましょう。

我々は決定しなければならないとき、もしARR [N] = -1、次いで、プルーフF(n)が算出されていない、そうでなければ、F(n)は上で計算された、及びf(N)= ARR [N]。直接の行に取り出した値に。コードは以下の通りであります:

 1 // 我们实现假定 arr 数组已经初始化好的了。
 2 	int f(int n){
 3    if(n <= 1){
 4        return n;
 5    }
 6    //先判断有没计算过
 7    if(arr[n] != -1){
 8        //计算过,直接返回
 9        return arr[n];
10    }else{
11        // 没有计算过,递归计算,并且把结果保存到 arr数组里
12        arr[n] = f(n-1) + f(n-1);
13        reutrn arr[n];
14    }
15 }

言い換えれば、再帰の使用が必要なときに
ダブルカウント、保存された状態が計算されなければならない場合はダブルカウントが存在するかどうかを検討する必要があります。

2.ボトムアップからあなたができるかどうかを検討します

再帰的な問題のために、我々は、一般的に再帰的なダウンから再帰的に、その後下、返された値を持つ層の層にまで。

しかし、時にはときnが比較的大きい、例えばn = 10000時間、あなた必要があります再帰的にダウン10000レベルからnまで<= 1だけゆっくりと、結果を返すnが大きすぎると、それは十分なスタック空間であってもよいです。

この場合、実際には、我々は、ボトムアップアプローチを検討することができます。例えば、私が知っています

F(1)= 1。

F(2)= 2。

その後、我々は、F(3)= F(2)+ F(1)= 3を推定することができます。放出は、F(4)、F(5)とf(N)までと同様であることができます。次のようにそのため、我々は、自己再帰的置換をボトムアップアプローチの使用を検討することができます。

 1	public int f(int n) {
 2       if(n <= 2)
 3           return n;
 4       int f1 = 1;
 5       int f2 = 2;
 6       int sum = 0;
 7
 8       for (int i = 3; i <= n; i++) {
 9           sum = f1 + f2;
10           f1 = f2;
11           f2 = sum;
12       }
13       return sum;
14   }

このように、実際には、としても知られている再帰的

締結

実際には、再帰はトップダウンから、常にではないですが、また、それらの多くはボトムアップからのもの、例えば、n = 1で、このような組み合わせのいくつかの並べ替えとして、N = 1000に再帰的になっています。

公開された107元の記事 ウォン称賛14 ビュー40000 +

おすすめ

転載: blog.csdn.net/belongtocode/article/details/103135426