二分木反復アルゴリズム

二分木反復アルゴリズム

バイナリ ツリーの事前順序、イン順序、および事後順序のトラバーサルは、アルゴリズムとデータ構造における基本的な問題です。再帰バイナリ ツリー トラバーサル アルゴリズムは、再帰の典型的な応用です。

バイナリ ツリー ノードが次のように定義されていると仮定します。

  1. 構造体ノード{
  2. int値;
  3. ノード *左;
  4. ノード *右;
  5. }

  1. void inorder_traverse(ノード *ノード) {
  2. if (NULL != ノード->left) {
  3. ノードを左にトラバースします。
  4. }
  5. 何かを実行します(ノード);
  6. if (NULL != node->right) {
  7. ノードを右にトラバースします。
  8. }
  9. }

事前順序トラバーサル アルゴリズムと事後順序トラバーサル アルゴリズムは似ています。

ただし、トラバーサル アルゴリズムだけでは不十分です。多くのアプリケーションでは、トラバーサル自体を抽象化する必要もあります。合計関数があると仮定すると、リンクリスト、配列、バイナリツリーなどのさまざまなデータ構造に適用できると期待されます。この時点で、イテレータの概念を抽象化し、イテレータを介してアルゴリズムとデータ構造を分離して、一般的なアルゴリズムをさまざまな種類のデータ構造に適用できるようになります。合計関数は次のように定義できます。

  1. int sum(イテレータ it)

線形構造として、リンクリストの反復子の実装は非常に単純で直感的ですが、バイナリツリーの反復子の実装はそれほど簡単ではありません。再帰トラバーサルを反復子に直接変換することはできません。その理由は、バイナリ ツリーの再帰トラバーサルが呼び出しスタック上でコンパイラによって自動的に実行され、プログラマーがこのプロセスを十分に制御できないためです。この場合、コールスタック全体のプッシュとポップを自分で制御できれば、制御の目的は達成されるのではないでしょうか。まず、二分木走査の非再帰アルゴリズムを見てみましょう。

  1. void inorder_traverse_nonrecursive(ノード *ノード) {
  2. スタックスタック;
  3. する{
  4. // ノードは現在処理中のサブツリーを表し、左の子をスタックの層ごとに下に押し下げます。これは再帰アルゴリズムの左サブツリーの再帰に対応します。  
  5. while (NULL != ノード) {
  6. スタックをプッシュします(ノード)。
  7. ノード = ノード->左;
  8. }
  9. する{
  10. ノード *top = stack.top();
  11. stack.pop(); // 再帰アルゴリズムの関数戻り値に対応して、スタックの先頭をポップアップします 
  12. 何かする(トップ);
  13. if (NULL != top->right) {
  14. node = top->right; //現在のサブツリーを、再帰アルゴリズムの右サブツリー再帰に対応する、走査したノードの右の子に設定します。  
  15. 壊す;
  16. }
  17. }
  18. スタックが空である間
  19. }
  20. スタックが空である間
  21. }

スタックベースの非再帰アルゴリズムを通じて、トラバーサル プロセスを制御できるようになりました。次に、これをイテレータとしてカプセル化する方法を検討します。 ここで重要なのは、トラバーサル プロセスがスタックの状態によって表されることを理解することです。したがって、反復子にはスタック構造が含まれている必要があり、各反復プロセスはスタックに対する操作であることは明らかです。イテレータ インターフェースが次のとおりであると仮定します。

  1. クラスイテレータ{
  2. 公共
  3. 仮想ノード* next() = 0;
  4. };

以下は、バイナリ ツリーの順序付きトラバーサル反復子の実装です。

  1. クラスInorderIterator:パブリックイテレータ {
  2. 公共
  3. ノードの順序を反復するノード *node) {
  4. ノード *current = ノード;
  5. while (NULL != 現在) {
  6. mStack.push(現在のスタック);
  7. current = current->left;
  8. }
  9. }
  10. 仮想ノード* next() {
  11. (mStack.empty())の場合{
  12. NULLを返します
  13. }
  14. ノード *top = mStack.top();
  15. mStack.pop();
  16. if (NULL != top->right) {
  17. ノード *current = top->right;
  18. while (NULL != 現在) {
  19. mStack.push(現在のスタック);
  20. current = current->left;
  21. }
  22. }
  23. 先頭に戻る;
  24. }
  25. プライベート
  26. std::stack<Node*> mStack;
  27. };

次に、このイテレータ実装の時間と空間の複雑さを見てみましょう。明らかに、スタックは最大ですべてのノードを格納する必要があるため、その空間計算量は O(n) です。時間の複雑さはどうでしょうか? next() の呼び出しでは最大 n 回のスタック操作が実行され、トラバーサル プロセス全体で next() を n 回呼び出す必要があるため、反復子全体の時間計算量は O(n^2) になりますか?答えはノーです!各ノードは 1 回だけプッシュおよびポップされるため、反復プロセス全体の時間計算量は依然として O(n) です。実際、これは再帰的トラバーサルの時間と空間の複雑さとまったく同じです。

コード実行の順序を制御するためにスタックを明示的に使用するほかに、yield セマンティクスをサポートする言語 (C#、Python など) ではより直接的なアプローチがあります。以下は、yield に基づいたバイナリ ツリーの順序付きトラバーサルの Python 実装です。

  1. // パイソン 
  2. def inorder(t):
  3. tの場合:
  4. inorder(t.left)内のxの場合:
  5. 収量 x
  6. 利回りt.ラベル
  7. x が inorder(t.right) 内にある場合:
  8. 収量 x

yield と return の違いに関する一般的な説明は、yield が戻ると、システムは関数呼び出しの状態を保持し、次に関数が呼び出されたときに、最後の実行ポイントから実行を継続するというものです。これは、スタック セマンティクスとはまったく異なるプロセス制御セマンティクスです。 Python のインタープリタは C で書かれていますが、C は yield セマンティクスをサポートしていません。では、インタープリタはどのようにして yield をサポートするのでしょうか? 再帰的トラバーサルを反復的トラバーサルに変換する上記の経験から、Python インタープリターが yield コードに何らかの変換を加えたに違いないと推測できたと思います。再帰を非再帰に変換できた場合は、yield コードを非 yield コードに変換するコンパイラーを作成してもよいでしょう。

オリジナルリンク: http://coolshell.cn/articles/9886.html

<<:  OpenGL ES 入門: 組み込み 3D グラフィックス アルゴリズム標準

>>:  優秀なプログラマーが開発効率を上げるために知っておくべき32のアルゴリズム

ブログ    
ブログ    
ブログ    
ブログ    

推薦する

500以上の研究と50以上のモデルを網羅したコードビッグモデルレビューがここにあります

BERT や GPT などの事前トレーニング済みのトランスフォーマーの登場により、言語モデリングは近...

飛んでくる花穂は人々を不安にさせますが、人と機械の組み合わせで不安を防ぐことができます!

「霧深い春の朝、緑の枝に雪の結晶が舞い散る。」さあ、また雪のように雪の結晶が舞い散る季節がやってき...

NVIDIA DLSS 3.5 がリリースされました!新しいAI「光再構成」は超リアルな光と影を実現し、新旧両方のグラフィックカードでサポートされています。

人工知能は世界を変えており、グラフィックス コンピューティングも例外ではありません。 5 年前、NV...

WeChatロボットの長期無料導入、初心者でも簡単にAIを始められる

以前、ローカルで WeChat ロボットを構築する方法を紹介しました。昨日、クラスメートから、ローカ...

人工知能と機械学習は、組織がデジタルシステムを運用する上でますます重要になる

[[280794]]いくつかの困難や障害にもかかわらず、多くの企業がデジタル変革プロジェクトで大きな...

AIが疫病と闘う:国家AIパイロットゾーンがその実力を発揮

ウイルス分析、ワクチン開発、医薬品研究開発から診断支援、スマート温度測定、AI消毒まで…新型コロナウ...

...

アルゴリズムやモデルがわかりませんか? UFIDA Jingzhi Industrial Brainは、産業インテリジェンスを簡単に習得する方法を教えます

現在、ビッグデータ、クラウドコンピューティング、人工知能技術が急速に発展しており、産業インターネット...

ハイエナが次世代トランスフォーマーになる? StripedHyena-7B オープンソース: 最大 128k の入力、トレーニング速度が 50% 向上

近年発表されたAIモデル、例えば言語、視覚、音声、生物学など各分野の大規模モデルは、Transfor...

...

魚眼カメラと超音波センサーの融合により、鳥瞰図による近距離障害物認識を実現

この記事は、Heart of Autonomous Driving の公開アカウントから許可を得て転...

なんて想像力豊かなんでしょう! AIは実際にこのようにプレイできます! 同意できない場合は、比較してみてください。

「まあまあ、今のところ需要はないんですが、ありがとうございます。」今週、子供向け番組を「販売」する...

Alibaba が MNNKit をオープンソース化: Android と iOS をサポートする MNN ベースのモバイル ディープラーニング SDK

最近、モバイル端末向けのディープラーニングフレームワークの開発がますます増えてきています。最近、アリ...

大規模機械学習のためのプログラミング手法、計算モデル、Xgboost および MXNet の事例

[[191977]]現在、機械学習のトレンドは、従来の方法のシンプルなモデル + 少量データ (手動...

ネットワークセキュリティにおける人工知能の4つの主要な応用シナリオ

セキュリティにおける人工知能の応用は、人々に 4 つの独自のセキュリティ上の利点をもたらします。この...