「アルゴリズムとデータ構造」二分木の美しさ

「アルゴリズムとデータ構造」二分木の美しさ

[[349809]]

序文

今回レビューする内容は、データ構造トピックの「ツリー」です。ツリーなどのデータ構造を見ると頭が痛くなる、理解しにくいという方は、この記事が参考になるかもしれません。

諺にあるように、何かを把握して理解したいのであれば、まずその概念や特性などを理解しなければなりません。

紹介ツリーは以下の点を中心に開発されています👇

木の基本概念

  • 基本用語
  • 木の種類
  • バイナリツリーの概念
  • 二分木の走査
  • 二分木問題の概要

マインドマップ 👇

木の基本概念

ツリーは、ツリーのような構造を持つデータ セットをシミュレートするために使用されます。あるいは、これを「抽象データ構造」、つまりこの抽象データ型を実装し、ツリーのような構造プロパティを持つデータ セットをシミュレートするために使用されるデータ構造と考えることもできます。

Wikipedia の定義によれば、次のように理解できるようです。

これは、n (n>0) 個の有限ノードで構成される階層セットです。根が上を向き、葉が下を向いている逆さまの木のように見えることから「木」と呼ばれています。以下の機能があります:

  • 各ノードには有限の数の子ノードが存在するか、子ノードが存在しません。
  • 親ノードを持たないノードはルートノードと呼ばれます。
  • ルート以外の各ノードには親ノードが 1 つだけあります。
  • ルート ノードを除き、各子ノードは複数の分離したサブツリーに分割できます。
  • ツリーにはサイクルがありません。

このとき、写真を取り出して見る必要があります👇

図から、上記の5つの特徴がよくまとめられます。

  • ノード A はルート ノードであり、親ノードがないため、ルート ノードになります。
  • ルート ノード (A) を除く他のすべてのノードには親ノードがあり、各ノードには限られた数の子ノードのみが存在するか、子ノードが存在しません。
  • あるノードから始まって、多くのサブツリーに分割することができます。例えば、ノード B から始まる場合がこれに該当します。

木についての知識が少し得られたので、次は木に関する用語をいくつか理解する必要があります。

基本用語


基本的なツリー用語

より正式な要約については、Wikipedia の説明をご覧ください。

  • 「ノード次数」: ノードに含まれるサブツリーの数はノードの次数と呼ばれます。
  • 「ツリー次数」: ツリーでは、最大ノード次数をツリー次数と呼びます。
  • 「リーフ ノード」または「ターミナル ノード」: 次数が 0 のノード。
  • 「非終端ノード」または「分岐ノード」: 次数がゼロでないノード。
  • 「父ノード」または「親ノード」: ノードに子ノードがある場合、このノードはその子ノードの親ノードと呼ばれます。
  • 「子ノード」または「サブノード」: ノードに含まれるサブツリーのルート ノードは、ノードの子ノードと呼ばれます。
  • 「兄弟ノード」: 同じ親ノードを持つノードは兄弟ノードと呼ばれます。
  • ノード「レベル」: ルートから始まり、ルートが最初のレベル、ルートの子ノードが 2 番目のレベル、というように続きます。
  • 「深さ」: 任意のノード n の場合、n の深さはルートから n までの一意のパスの長さであり、ルートの深さは 0 です。
  • 「高さ」: 任意のノード n の場合、n の高さは n からリーフまでの最長パスであり、すべてのリーフの高さは 0 です。
  • 「いとこノード」: 親ノードが同じレイヤーにあるノードは互いにいとこです。
  • 「ノードの祖先」: ルートからノードまでのブランチ上のすべてのノード。
  • 「子孫」: ノードをルートとするサブツリー内のノードは、そのノードの子孫と呼ばれます。
  • 「フォレスト」: 互いに素な m (m>=0) 本のツリーの集合をフォレストと呼びます。

上記の図を組み合わせることで、これらの概念を理解することができます。 2 つを組み合わせることで、ツリーをより深く理解できるようになります。

上記の基本的な概念といくつかの専門用語を使用して、木を分類し、どのような種類の木があるのか​​を確認する必要があります。

木の種類

木の概念と基本的な用語を理解したので、次に木の種類について詳しく説明します。

分類の基準としてWikipediaを使うことができます👇

  • 順序なしツリー: ツリー内のどのノードの子ノード間にも順序関係はありません。この種類のツリーは順序なしツリー、またはフリー ツリーと呼ばれます。
  • 順序付きツリー: ツリー内の任意のノードの子ノード間には順序関係があります。この種類のツリーは順序付きツリーと呼ばれます。
  • 完全二分木: 二分木の場合、その深さは d (d>1) であると仮定します。 d 番目の層を除いて、他の層のノードの数は最大値に達しており、d 番目の層のすべてのノードは左から右に連続して密に配置されています。このような二分木は完全二分木と呼ばれます。
  • バランス二分木 (AVL 木): 任意のノードの 2 つのサブツリー間の高さの差が 1 以下である場合にのみ二分木になります。
  • ソート済みバイナリ ツリー (英語: Binary Search Tree): バイナリ検索ツリー、順序付きバイナリ ツリーとも呼ばれます。
  • 完全二分木: すべてのリーフ ノードが最下層にある完全な二分木。
  • 二分木: 各ノードに最大 2 つのサブツリーが含まれるツリーを二分木と呼びます。
  • ハフマン ツリー: 最短の重み付きパスを持つ二分木は、ハフマン ツリーまたは最適二分木と呼ばれます。
  • B ツリー: 読み取りおよび書き込み操作に最適化され、データを順序どおりに維持でき、2 つ以上のサブツリーを持つ自己バランス型バイナリ検索ツリー。

ツリーの分類が非常に多いため、それらを 1 つずつ習得する必要がありますか? 個人的には、最も単純で最も広く使用されているツリーの種類でもあるバイナリ ツリーの構造を習得すれば十分だと思います。

次に、バイナリツリーを紹介します。

二分木の概念

バイナリツリーは典型的なツリー構造です。名前が示すように、バイナリ ツリーは、各ノードに最大 2 つのサブツリー (通常は「左サブツリー」と「右サブツリー」と呼ばれます) が含まれるツリー構造です。


バイナリツリー

この図の内容から判断すると、バイナリツリーの構造が明確に示されているはずです。

二分木の性質については、補足知識として下の図を参考にしてください。個人的には、ここがポイントではないと思います。


二分木の特性

重要なのは、そのトラバーサル方法を習得する必要があるということです。

二分木の走査

バイナリ ツリーをトラバースする一般的な方法は次の 3 つであることがわかっています。

  • 事前順序トラバーサル
  • 順序通りの走査
  • その後のトラバース

どのようなトラバーサル手法でも、非再帰バージョンだけでなく、基本的なアルゴリズム スキルをテストする再帰バージョンも習得する必要があります。次に、再帰バージョンについて見てみましょう。

事前順序トラバーサル

バイナリツリーの事前順序走査を練習するにはここをクリックしてください

バイナリ ツリーのルート ノードが指定されると、そのノード値の「事前順序」トラバーサルが返されます。

偽のデータを模擬するとします👇

  1. 入力: [1, null ,2,3]
  2. 1
  3. \
  4. 2
  5. /
  6. 3
  7. 出力: [1,3,2]

次に、事前順序探索の理解に基づいて、問題を解決するための疑似コードを書くことができます👇

  1. // 天天アップ
  2. // *関数TreeNode(val, left , right ) {
  3. // * this.val = (val===undefined ? 0 : val)
  4. // * this.left = ( left === undefined ? null : left )
  5. // * this.right = ( right === undefined ? null : right )
  6. // * }
  7. inorderTraversal = (root, arr = []) とします => {
  8. if(ルート) {
  9. inorderTraversal(ルート.left , arr)
  10. arr.push(ルート.値)
  11. inorderTraversal(ルート.right , arr)
  12. }
  13. リターン
  14. }

非再帰バージョン 👇

非再帰の場合、ノードを格納するためのデータ構造を使用する必要があります。使用する必要があるのはスタックです。そのアイデアは参考になります👇

  • ルートノードはターゲットノードであり、その子ノードへのトラバーサルが開始されます。
  • 1. ターゲットノードにアクセスする
  • 2. 左の子をスタックにプッシュします -> 左の子が空になるまで
  • 3. スタックからノードをポップし、右の子をターゲットノードとして取得し、1、2、3を順番に実行します。
  1. preorderTraversal = (root, arr = []) => {を設定します。
  2. 定数スタック = []、res = []
  3. 現在の値をルートとする
  4. while(現在|| スタックの長さ > 0) {
  5. while (現在) {
  6. res.push(現在の.val)
  7. stack.push(現在の)
  8. 現在=現在. 
  9. }
  10. 現在の= stack.pop()
  11. 現在=現在. 
  12. }
  13. 戻り
  14. }

順序通りの走査

バイナリ ツリーが与えられた場合、その順序どおりのトラバーサルを返します。

例:

  • 入力: [1,null,2,3] 1
  • 23
  • 出力: [1,3,2]

上級: 再帰アルゴリズムは単純ですが、反復アルゴリズムで実行できますか?

再帰バージョン 👇

  1. const inorderTraversal = (ルート、arr = []) => {
  2. if(ルート) {
  3. inorderTraversal(ルート.left , arr)
  4. arr.push(ルート.val)
  5. inorderTraversal(ルート.right 、 arr)
  6. }
  7. リターン
  8. }

非再帰バージョンについてはここでは説明しません。これは、事前順序トラバーサルと同じ考え方で、スタックを使用してノード情報を維持します。

  1. const inorderTraversal = (ルート、arr = []) => {
  2. 定数スタック = []、res = []
  3. 現在の値をルートとする
  4. while(現在|| スタックの長さ > 0) {
  5. while (現在) {
  6. stack.push(現在の)
  7. 現在=現在. 
  8. }
  9. 現在の= stack.pop()
  10. res.push(現在の.val)
  11. 現在=現在. 
  12. }
  13. 戻り
  14. }

その後のトラバース

バイナリ ツリーが与えられた場合、その後順トラバーサルを返します。

例:

  • 入力: [1,null,2,3]
  • 1
  • 23
  • 出力: [3,2,1]

上級: 再帰アルゴリズムは単純ですが、反復アルゴリズムで実行できますか?

出典: LeetCode リンク: https://leetcode-cn.com/problems/binary-tree-postorder-traversal 著作権は LeetCode Network に帰属します。商用目的での複製については、正式な許可を得るためにお問い合わせください。非商用目的での複製については、出典を明記してください。

再帰バージョン 👇

  1. const postorderTraversal = (ルート、arr = []) => {
  2. if(ルート) {
  3. postorderTraversal(ルート.left , arr)
  4. postorderTraversal(ルート.right 、 arr)
  5. arr.push(ルート.val)
  6. }
  7. リターン
  8. }

非再帰バージョン 👇

実は、最初の2つをやると、ルーチンがあることがわかりますよ〜

  1. const postorderTraversal = (ルート、arr = []) => {
  2. 定数スタック = []、res = []
  3. let current = root, last = null //最後のポインタは前のノードを記録します
  4. while(現在|| スタックの長さ > 0) {
  5. while (現在) {
  6. stack.push(現在の)
  7. 現在=現在. 
  8. }
  9. 現在のスタック = [スタックの長さ - 1]
  10. if (! current . right || current . right == last ) {
  11. 現在の= stack.pop()
  12. res.push(現在の.val)
  13. 最後=現在 
  14. current = null // スタックのポップを続行
  15. }それ以外{
  16. 現在=現在. 
  17. }
  18. }
  19. 戻り
  20. }

バイナリツリーのレベルトラバーサル ⭐⭐

リンク: 二分木のレベル順走査

バイナリツリーが与えられた場合、「レベル順トラバーサル」によって取得されたノード値を返します。 (つまり、すべてのノードを左から右へ、レイヤーごとに訪問します)。

例: バイナリツリー: [3,9,20,null,null,15,7],

  • 3
  • /
  • 9 20 /
  • 15 7

レベルトラバーサルの結果を返します:

  • [ [3]、[9,20]、[15,7] ]

出典: LeetCode リンク: https://leetcode-cn.com/problems/binary-tree-level-order-traversal 著作権は LeetCode Network に帰属します。商用目的での複製については、正式な許可を得るためにお問い合わせください。非商用目的での複製については、出典を明記してください。

再帰バージョン 👇

  1. const levelOrder =関数(ルート) {
  2. if(!root) return []
  3. res = [] とします
  4. dfs(ルート, 0, res)
  5. 戻り
  6. }
  7.  
  8. 関数dfs(ルート、ステップ、res){
  9. if(ルート){
  10. if (!res[ステップ]) res[ステップ] = []
  11. res[ステップ].push(ルート.val)
  12. dfs(ルート.左, ステップ+1, res)
  13. dfs(ルート.right , ステップ+1, res)
  14. }
  15. }

非再帰バージョン 👇

ここで使用されるのは、キュー データ構造と先入れ先出しメカニズムです。

  1. const レベルオーダー = (ルート) => {
  2. キュー = []、res = [] とします。
  3. if (root) queue.push(root);
  4. while (キューの長さ) {
  5. next_queue = [] とします。
  6. 今_res = []
  7. while (キューの長さ) {
  8. ルート = キュー.shift()
  9. now_res.push(ルート.val)
  10. root.left && next_queue.push( root.left )
  11. root.right && next_queue.push( root.right )
  12. }
  13. キュー = 次のキュー
  14. res.push(now_res)
  15. }
  16. 戻り
  17. }

トピックの概要

前にも言ったように、すべての問題を解けない場合は、残りの部分は LeetCode の練習に頼ることができます。また、一般的なバイナリ ツリーの問題セットもいくつか用意しました。問題の質は依然として良好です👇

  • 二分木の最小深さ⭐
  • 二分木の最大深さ⭐
  • 同じ木⭐
  • 二分探索木の範囲と⭐
  • 対称二分木 ⭐
  • ソートされた配列を二分探索木に変換する⭐
  • 二分木レベルトラバーサル II ⭐⭐
  • 二分木の最小共通祖先 ⭐⭐
  • 二分探索木を検証する ⭐⭐
  • パス合計 III ⭐⭐
  • 繰り返し要素があります III ⭐⭐
  • 現在の要素より小さい右側の要素の数を計算します⭐⭐⭐

<<:  人工知能と機械学習: フィンテック業界の新たな青写真

>>:  科学技術の力を感じる: 人工知能とスマートヘルスケアの 4 つの注目のアプリケーションの分析

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

推薦する

AGVロボットマルチエージェント経路探索の4つの主要な研究方向

マルチエージェント経路探索 (MAPF) は、人工知能、ロボット工学、理論計算機科学、実践的オペレー...

医療用人工知能の分野は新たな状況を迎え、テクノロジー大手は積極的に導入を進めている。

報告書によると、医療における人工知能の主な応用分野の一つである医療ロボットの市場規模は2019年に4...

...

...

AI を活用して経費管理におけるバイアス問題を解決する方法

新型コロナウイルス感染症のパンデミックによってもたらされた変化の中で、組織の業務が在宅勤務からリモー...

最新レポート: 従業員の 25% が ChatGPT などの AI ツールに機密データをアップロードしている

新たな調査によると、従業員の15%がChatGPTに会社のデータを頻繁にアップロードしており、そのデ...

研究:インターネットには低品質の機械翻訳コンテンツが溢れており、大規模な言語モデルのトレーニングではデータの罠に注意する必要がある

2月4日、アマゾンクラウドコンピューティング人工知能研究所の研究者らは、インターネット上の大量のコン...

2つのセッションでは人工知能技術が注目を集めました。AI技術はこれらの業界で導入されています

近年、人工知能がブームを迎えており、人々は合理的な分析と思考を通じて、人工知能の波をどのように利用し...

光速画像認識について学ぶ: 1ナノ秒未満

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

人工知能温度測定が「スタンドガード」に登場!立ち止まる必要がなく、複数人が同時に温度を測定できます

この期間中、自宅に留まっている人々は、定期的にスーパーマーケットに行って商品を購入するという問題にも...

プログラマーはどのようにして人工知能を学ぶのでしょうか? 2019 年の人工知能の給与見通しはどうでしょうか?

2019年の人工知能の給与水準、まずは全体の給与水準の2つの分析グラフを見てみましょう! ***は...

...

ボストンのロボットが話題になった後、別のヒューマノイドロボットがデビューした

10年以上前、テヘラン大学の研究者らは、Surenaと呼ばれる原始的なヒューマノイドロボットを発表し...

...

...