JS を使用してバイナリ ツリー トラバーサル アルゴリズムのサンプル コードを実装する

JS を使用してバイナリ ツリー トラバーサル アルゴリズムのサンプル コードを実装する

序文

コンピュータ サイエンスでは、ツリーは広く使用されている抽象データ型 (ADT) であり、非線形データ構造の一種です。ツリー、特にバイナリツリーはコンピューター分野で広く使用されています。

ツリー関連の概念:

  • ノード: 各要素はノードと呼ばれる
  • ツリールート: ルートノード
  • 次数: ノードに含まれる子ノードの数をノードの次数と呼びます。
  • リーフノード: 次数0のノード

1. バイナリツリー

概念: 各ノードに最大 2 つのサブツリーが含まれるツリーは、バイナリ ツリーと呼ばれます。

1.1. 二分木の走査

バイナリ ツリーのトラバーサルには、深さトラバーサルと幅トラバーサルの 2 種類があります。深さトラバーサルには、事前順序、インオーダー、事後順序の 3 つのトラバーサル方法があります。 幅方向のトラバーサルは、レイヤーごとに順番にトラバーサルするレベル方向のトラバーサルです。

4 つのトラバーサルの主なアイデアは次のとおりです。

  • 事前順序: 最初にルートにアクセスし、次に左のサブツリーにアクセスし、最後に右のサブツリーである DLR にアクセスします。
  • 順序どおり: 最初に左のサブツリーにアクセスし、次にルートにアクセスし、最後に右のサブツリー (LDR) にアクセスします。
  • 後順序: 最初に左のサブツリーにアクセスし、次に右のサブツリーにアクセスし、最後にルート LRD にアクセスします。
  • 幅: レイヤーごとに順番にトラバースします。

たとえば、a+b*(cd)-e/f という式はバイナリ ツリーで表されます。

それらを個別にトラバースします。

  • 前文: -+a*b-cd/ef
  • 順番:a+b*cde/f
  • 追記:abcd-*+ef/-
  • 幅: -+/a*efb-cd

1.2. jsを使用してバイナリツリーを表現する

バイナリ ツリーを表すには、js オブジェクトを使用します。オブジェクトには、left、value、right の 3 つのプロパティがあり、それぞれ左サブツリー、値、右サブツリーを表します。上記の例 a+b*(cd)-e/f は、js を使用して次のように表すことができます。

var ツリー = {
    価値: '-'、
    左: {
        値: '+',
        左: {
            値: 'a'
        },
        右:
            価値: '*'、
            左: {
                値: 'b'
            },
            右:
                価値: '-'、
                左: {
                    値: 'c'
                },
                右:
                    値: 'd'
                }
            }
        }
    },
    右:
        価値: '/'、
        左: {
            値: 'e'
        },
        右:
            値: 'd'
        }
    }
}

1.3 事前順序探索アルゴリズム

前書き: 方法は 2 つあります。最初の方法は非常に簡単で、再帰を直接使用する方法です。

関数 preOrder(treeNode) {
  if(ツリーノード) {
    console.log(treeNode.value); // 出力は、このノードにアクセスすることを意味します preOrder(treeNode.left);
    ツリーノードを右にプリオーダーします。
  }
}

アルゴリズムは非常にシンプルです。まずルート ノードをトラバースし、次に左のサブツリーを再帰的にトラバースします。左のサブツリーをトラバースした後、右のサブツリーを再帰的にトラバースします。

2番目の非再帰的走査

関数 preOrder(treeNode) {
  if(ツリーノード) {
    var stack = [treeNode]; // バイナリツリーをスタックにプッシュします while (stack.length !== 0) {
      treeNode = stack.pop(); // スタックの先頭を取得します。 document.getElementById('text').appendChild(document.createTextNode(treeNode.value)); // ノードにアクセスします。 if(treeNode.right) stack.push(treeNode.right); // 右のサブツリーをスタックにプッシュします。 if(treeNode.left) stack.push(treeNode.left); // 左のサブツリーをスタックにプッシュします。 }
  }
}

2 つ目は、スタックの考え方を使うことです。ご存知のとおり、スタックは先入れ後出しのデータ構造です。最初にルート ノードをスタックにプッシュし、次にスタックの先頭を取り、ルート ノードにアクセスして、右と左のサブツリーをそれぞれスタックにプッシュします。最初に左側からアクセスする必要があるため、右側を最初にスタックにプッシュする必要があります。そのため、最初に右側のサブツリーをスタックにプッシュし、次にスタックが空になるまでスタックを取り出すループを実行します。

1.4. 順序探索アルゴリズム

順序再帰アルゴリズム:

関数InOrder(treeNode) {
    if(ツリーノード) {
        ツリーノードの左端に順序を付けます。
        document.getElementById('text').appendChild(document.createTextNode(treeNode.value));
        ツリーノードの右端に順序を付けます。
    }
}

事前順序再帰アルゴリズムと同じ考え方だが、訪問したノードの位置が異なる

2番目のタイプ:

関数InOrder(ノード) {
  if(ノード) {
    var stack = []; // 空のスタックを作成する // スタックが空でないかノードが空でない場合は、while (stack.length !== 0 || node) { をループします。 
      if (node) { //ノードが空でない場合 stack.push(node); //ノードをスタックにプッシュします node = node.left; //左のサブツリーを現在のノードとして使用します } else { //左のサブツリーが空です。つまり、左のサブツリーはありません node = stack.pop(); //ノードを取り出します document.getElementById('text').appendChild(document.createTextNode(node.value));
          node = node.right; // 右ノードを現在のノードとして使用します}
    }
  }
}

非再帰的順序アルゴリズムの考え方は、現在のノードをスタックにプッシュし、次に左のサブツリーをトラバースし、左のサブツリーが存在する場合は、左のサブツリーが空になるまでスタックにプッシュし続け、前のノードにアクセスし、右のサブツリーをスタックにプッシュすることです。

1.5. 後順序探索アルゴリズム

1つ目: 再帰的トラバーサルアルゴリズム

関数 postOrder(ノード) {
    if (node) { // バイナリツリーが空かどうかを判断します postOrder(node.left); // 左のサブツリーを再帰的にトラバースします postOrder(node.right); // 右のサブツリーを再帰的にトラバースします document.getElementById('text').appendChild(document.createTextNode(node.value));
    }
}

2番目: 非再帰的トラバーサルアルゴリズム

関数 postOrder(ノード) {
    if (node) { //バイナリツリーが空かどうかを判断var stack = [node]; //バイナリツリーをスタックにプッシュvar tmp = null; //キャッシュ変数を定義while (stack.length !== 0) { //スタックが空でない場合はループしますtmp = stack[stack.length - 1]; //スタックの最上位の値をtmpに保存if (tmp.left && node !== tmp.left && node !== tmp.right) { //左のサブツリーがある場合、node !== tmp.left && node !== tmp.right は、左と右のサブツリーを繰り返しスタックにプッシュすることを回避するためですstack.push(tmp.left); //左のサブツリーノードをスタックにプッシュします} else if (tmp.right && node !== tmp.right) { //ノードに右のサブツリーがある場合stack.push(tmp.right); //右のサブツリーをスタックにプッシュします} else {
                document.getElementById('text').appendChild(document.createTextNode(stack.pop().value));
                ノード = tmp;
            }
        }
    }
}

ここでは、最後にポップおよびプッシュされたノードを記録するために tmp 変数が使用されます。アイデアとしては、まずルート ノードと左のツリーをスタックにプッシュし、次に左のツリーを取り出し、右のツリーをプッシュして取り出し、最後にルート ノードを取り出すというものです。

以下は、このアルゴリズムを使用して前のバイナリツリーをトラバースするプロセスです。

スタック tmp ノード プリント初期値: - null -
ラウンド1: -+ - -
第2ラウンド: -+a + -
ラウンド3: -+ a a a
第4ラウンド: -+* + a
第5ラウンド: -+*b * a
第6ラウンド: -+* b b b
第7ラウンド: -+*- * b
第8ラウンド: -+*-c - b
第9ラウンド: -+*- c c c
第10ラウンド: -+*-d - c
第11ラウンド: -+*- d d d
第12ラウンド: -+* - - -
第13ラウンド: -+ * * *
第14ラウンド: - + + +
ラウンド15: -/-+
ラウンド16: -/e / +
第17ラウンド: -/ e e e
第18ラウンド: -/f / e
第19ラウンド: -/ f f f
第20ラウンド: - / / /
第21ラウンド: - - -

結果: abcd-*+ef/-

1.6. レイヤーごとのトラバーサルアルゴリズム

関数breadthTraversal(ノード) {
    if (node) { //バイナリツリーが空かどうかを判断var que = [node]; //バイナリツリーをキューに入れますwhile (que.length !== 0) { //キューが空かどうかを判断node = que.shift(); //キューからノードを取得しますdocument.getElementById('text').appendChild(document.createTextNode(node.value)); //取得したノードの値を配列に保存しますif (node.left) que.push(node.left); //左のサブツリーが存在する場合は、左のサブツリーをキューに入れますif (node.right) que.push(node.right); //右のサブツリーが存在する場合は、右のサブツリーをキューに入れます}
    }
}

配列を使用してキューをシミュレートし、最初にルート ノードをキューに配置します。キューが空でない場合は、ループを実行します。キューからノードを取り出し、ノードに左サブツリーがある場合は、ノードの左サブツリーをキューに格納します。ノードに右サブツリーがある場合は、ノードの右サブツリーをキューに格納します。

2. アルゴリズムに関する質問

1.1. 二分木の最大深さ

バイナリツリーが与えられた場合、その最大深さを見つけます。

バイナリ ツリーの深さは、ルート ノードから最も遠いリーフ ノードまでの最長パス上のノードの数です。

たとえば、次のバイナリ ツリーは深さ 3 を返します。

3
/ \
9 20
/ \
15 7

定数ツリー = {
値: 3,
左: {
値: 9
},
右:
値: 20,
左: { 値: 15 },
右: { 値: 9 }
}
}

再帰アルゴリズム:再帰アルゴリズムの考え方は非常にシンプルです。まず、左のサブツリーの最も深い層を取得し、次に右のサブツリーの最も深い層を取得し、それらの最大値がツリーの深さになります。

var maxDepth = function(ルート) {
  ルートの場合
    0を返します。
  }
  定数 leftDeep = maxDepth(root.left) + 1;
  定数 rightDeep = maxDepth(root.right) + 1;
  Math.max(leftDeep, rightDeep) を返します。
};
/*
最大深度(ルート) = 最大深度(ルート.左) + 1 = 2
最大深度(ルート.左) = 最大深度(ルート.左.左) + 1 = 1
最大深度(ルートの左端) = 0;

最大深度(ルート) = 最大深度(ルート.右) + 1 = 3
最大深度(ルート.右) = 最大深度(ルート.右.右) + 1 = 2
最大深度(ルート.右.右) = 最大深度(ルート.右.右.右) + 1 = 1
最大深度(ルート右右右) = 0
*/

1.2. 二分木のすべてのパス

バイナリ ツリーが与えられた場合、ルート ノードからリーフ ノードまでのすべてのパスを返します。

例えば:

3
/ \
9 20
/ \
15 7

戻り値: ['3->9', '3->20->15', '3->20->7']

再帰メソッドの使用:

var binaryTreePaths = function(ルート) {
  if (!root) は [] を返します。
  定数res = [];
  関数 dfs(curNode, curPath) {
    if(!curNode.left && !curNode.right) {
      res.push(curPath);
    }
    左のノードの場合
      dfs(curNode.left, `${curPath}->${curNode.left.value}`)
    }
    右のノードの場合
      dfs(curNode.right、`${curPath}->${curNode.right.value}`)
    }
  }
  dfs(ルート、`${root.value}`);
  res を返します。
};

要約する

JS を使用してバイナリ ツリー トラバーサル アルゴリズムを実装する方法に関するこの記事はこれで終わりです。JS バイナリ ツリー トラバーサル アルゴリズムに関する関連コンテンツをさらにご覧になりたい場合は、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript を使用してソートアルゴリズムを実装する方法
  • Matlab による JavaScript プログラミング、重心アルゴリズムによる位置決め学習
  • JavaScript 初心者のための二分探索木アルゴリズムのチュートリアル
  • JavaScript で実装された 7 つのソート アルゴリズムの概要 (推奨!)
  • JavaScript でツリー構造を構築するための効率的なアルゴリズムについての簡単な説明
  • JavaScript でアルゴリズムの複雑さを学ぶ方法
  • jsは赤い封筒の順序と量を指定するアルゴリズムを実装します
  • JavaScript を使用して簡単なアルゴリズムを実行する方法

<<:  Webフォーム作成スキル

>>:  MySQL マスタースレーブレプリケーションと読み取り書き込み分離の詳細な説明

推薦する

WeChatアプレットがシンプルな計算機機能を実装

この記事では、WeChatアプレットの計算機機能を実装するための具体的なコードを参考までに紹介します...

MySQLはgroup_concat()関数に基づいて複数のデータ行を結合します

非常に便利な機能group_concat() について、マニュアルには次のように記載されています: ...

LINUX での IPTABLES ファイアウォールの基本的な使用方法のチュートリアル

序文パブリック IP を持つ本番 VPS の場合、必要なポートのみが開かれ、IP とポートを制御する...

Nginx proxy_redirect の使用方法の詳細な説明

今日、Apache の nginx リバース プロキシを実行していたときに、ちょっとした問題に遭遇し...

Vue で変数式セレクターを実装する方法

目次HTML構造の定義入力タグのバインディング属性入力タグはキーダウンイベントをリッスンしますli ...

dockerプライベート倉庫の構築と利用の詳細説明

1. リポジトリイメージをダウンロードする docker プルレジストリ 2. プライベートウェアハ...

Linux で open-vswitch をインストールおよびアンインストールする方法

1. ソースコードからovsをコンパイルしてインストールします。依存関係をインストールします: # ...

MySQL 5.7 をインストールした後にコマンドライン ウィンドウを開くとクラッシュする問題の解決方法

序文最近、MySQL 5.7 をインストールしましたが、問題が見つかりました。コマンド ライン ウィ...

CSS で「プラス記号」効果を実装するためのサンプルコード

以下に示すプラス記号の効果を実現するには: この効果を実現するには、div 要素だけが必要です。 b...

MySQL 5.7.17 のインストールと使用方法のグラフィックチュートリアル

MySQL は、スウェーデンの会社 MySQL AB によって開発され、現在は Oracle が所有...

JavaScript WeakMap の使い方の詳しい説明

WeakMap オブジェクトは、キーが弱参照であるキー/値のペアのコレクションです。キーはオブジェク...

Reactにおけるコンポーネント通信の詳細な説明

目次親コンポーネントは子コンポーネントと通信します子コンポーネントは親コンポーネントと通信しますコン...

JSはリクエストディスパッチャーを実装する

目次抽象化と再利用シリアルセグメントシリアル、セグメントパラレル要約するはじめに: JS は当然並列...

JavaScript の基礎におけるデータ型の詳細な説明

目次1. データ型1.1 なぜデータ型が必要なのか? 1.2 変数のデータ型1.3 データ型の分類2...

Docker で Oracle 11g イメージ構成をプルダウンする際の問題を分析する

1. イメージをプルするdocker pull レジストリ.cn-hangzhou.aliyuncs...