みなさん、こんにちは!私は[AI菌]、
不熬夜
プログラマ。私热爱AI、热爱分享、热爱开源
!このブログは私の要約であり、学習についての考察です。あなたも深度学习、机器视觉、算法、Python、C++
興味があるなら、あなたは私のダイナミックに集中することができます、私たちは一緒に学び、一緒に進歩します〜
木と言えば、誰もが木に慣れ親しんでいるということは、結局のところ、日常生活の中で一般的なことです。しかし、今日の主役はツリーではありません。データ構造内のツリー、バイナリツリー、バイナリサーチツリー、およびそれらの基本的な操作について話しましょう。
記事ディレクトリ
1つ、ツリーとバイナリツリー
1.1木とは?
ツリーはデータ構造であり、n(n> = 1)個の有限ノードで構成される階層的なコレクションです。逆さまの木のように見えるため、「木」と呼ばれます。つまり、根が上を向き、葉が下を向きます。
ツリーはノードとエッジで構成され、リングにはデータ構造がありません。次の図から、ツリーの構造をより直感的に理解できます。
ツリーは、再帰的な定義の特性を満たします。つまり、データ構造がツリー構造の場合、ルートノードを削除すると、いくつかのサブ構造もツリーになり、通常はサブツリーと呼ばれます。
ツリーでは、ノード間の階層関係に応じて、ノードの名前も異なります。次の図に示すように、次のツリーを見てみましょう。
さまざまなノードの関係と名前は次のとおりです。
- 上位レベルのノードはノードBとノードCであり、AとC Bは親ノード、A、B、Cは子ノードです。
- BとCの両方がAの「子」である場合、BとCは兄弟ノードと呼ばれます。
- Aに親ノードがない場合、Aはルートノードと呼ばれます。
- ノードG、H、I、Fには子ノードがないため、G、H、I、Fはリーフノードと呼ばれます。
ツリーがある場合は、深度とレイヤーを使用して、ツリー内のノードの場所を説明する必要があります。上の図に示すように、ノードのレベルはルートノードからカウントされます。
- ルートは次のような最初のレベルです:A
- ルートの「子」は、B、Cなどの第2レベルです
- ルート「子」の「子」は、D、E、Fなどの第3レベルです
- 同様に、4番目のレイヤー(最後のレイヤー)は、G、H、Iです。
ツリー内のノードのレイヤーの最大数は、ツリーの深さ(深さと呼ばれ、高さとも呼ばれます)であるため、これは深さ4のツリーです。
1.2二分木とは何ですか?
大規模なツリーファミリーには、頻繁に使用される特別なツリーがあり、これはバイナリツリーです。バイナリツリーでは、各ノードには最大で2つのブランチがあります。つまり、各ノードには最大で2つの子ノードがあり、それらは左の子ノードと右の子ノードと呼ばれます。
バイナリツリーには、次の図に示すように、2つの最も特殊なタイプがあります。
- 完全なバイナリツリーは、リーフノードに2つの子ノードがある以外のすべてのノードとして定義されます。
- 完全な二分木は、最後の層を除く他の層のノードの最大数として定義され、最後の層の葉ノードはすべて左側に配置されます。
完全なバイナリツリーは完全に見えませんが、なぜそれをそのように呼ぶのですか?これは、実際にはバイナリツリーの格納に関連しています。バイナリツリーを格納する方法は2つあります。1つはポインタベースのチェーンストレージメソッドで、もう1つは配列ベースのシーケンシャルストレージメソッドです。
- チェーンストレージメソッドはリンクリストに似ています。次の図に示すように、各ノードには3つのフィールドがあり、1つはデータを格納し、他の2つは左と右の子ノードへのポインターを格納します。
- 逐次格納方式は、下図のようにノードを法則に従って配列に格納する方式であり、計算の便宜上、添え字が1の位置にルートノードを配置することに同意します。続いて、ノードBは下付き文字2の位置に格納され、ノードCは下付き文字3の位置に格納されます。
完全二分木と呼ばれるのは、記憶空間の利用効率の観点からです。完全なバイナリツリーの場合、無駄なのは添え字0の格納場所だけです。それが不完全なバイナリツリーである場合、多くのストレージスペースが無駄になります。
次の図に示す不完全なバイナリツリーでは、5と6の位置を予約する必要があります。同時に、2つの子ノード10と11の5の位置と2つの子ノード12と13の6の位置。このような二分木は、アレイの記憶領域を完全には利用しません。
次に、ツリーの前順、中次、および後順のトラバーサル
次に、二分木を例にとり、ツリーの操作を紹介しますが、他の種類のツリーの操作も二分木と基本的に同じです。
これまでに学習したデータ構造はすべて「1対1」の関係であることがわかります。つまり、以前のデータは、リンクリスト、スタック、キューなど、次のデータとの接続関係しかありません。ツリー構造は「1対多」の関係です。つまり、前の親ノードには、以下のいくつかの子ノードとの接続関係があります。
「1対1」の構造では、検索には特定の値があり、各データを直接順番にたどることができます。ただし、ツリーは「1対多」の関係なので、どの順序でトラバースする必要がありますか?
実際、ツリーをトラバースするための3つの非常に古典的な方法があり、前順トラバーサル、中次トラバーサル、および後順トラバーサルです。ここでの順序とは、親ノードのトラバース順序を指します。最初の順序は最初に親ノードをトラバースすることであり、中央の順序は親ノードを中央でトラバースすることであり、後者の順序は親ノードを最後にトラバースすることです。
どのようなトラバーサルであっても、再帰呼び出しによって行われます。以下に示すように:
- preorder traversalは、ツリー内の任意のノードについて、最初にこのノードを出力し、次にその左サブツリーをpreorderでトラバースし、最後にその右サブツリーをpreorderでトラバースします。
- Middle-order traversalは、ツリー内の任意のノードについて、最初にその左側のサブツリーを中央の順序でトラバースし、次にこのノードを出力し、最後にその右側のサブツリーを中央の順序でトラバースします。
- ツリー内の任意のノードのポストオーダートラバーサルは、左のサブツリーを順にトラバースし、次に右のサブツリーをポストオーダーして、最後にそれ自体を出力します。
三、二分探索木の特徴
特別なプロパティのないバイナリツリーの場合、トラバーサルの時間の複雑さは別として、実際に追加および削除操作を実行する時間の複雑さはO(1)です。ツリーデータの検索操作はリンクリストと同じですが、各データをトラバースして判断する必要があるため、時間の複雑さはO(n)です。
ただし、バイナリツリーにいくつかの特性(バイナリサーチツリーなど)がある場合、これらの特性を使用して時間の複雑さを軽減できます。
バイナリ検索ツリー(バイナリ検索ツリーとも呼ばれます)には、次の特性があります。
- 二分探索木のどのノードでも、左側のサブツリーの各ノードの値はこのノードの値より小さくなければならず、右側のサブツリーの各ノードの値はこれより大きくなければなりません。ノードの値。
- 二分探索木では、2つのノードの値が等しいという状況は可能な限り回避されます。
- バイナリ検索ツリーを順番にたどることにより、順序付けられたデータキューを小さなものから大きなものへと出力できます。次の図に示すように、順序トラバーサルの結果は10、13、15、16、20、21、22、26です。
したがって、二分探索木は、ルールでソートされた二分木と簡単に考えることができます。
第四に、二分探索木の追加・削除・確認操作
3.1検索操作
二分探索木を使用して探索操作を実行する場合、以下の判断を下すことができます。
- まず、ルートノードが検索するデータと等しいかどうかを判断し、等しい場合は返します。
- ルートノードが検索するデータよりも大きい場合、検索は左側のサブツリーでリーフノードまで再帰的に実行されます。
- ルートノードが検索対象のデータよりも小さい場合、検索は、リーフノードまで右側のサブツリーで再帰的に実行されます。
このような「バイナリ検索」によって消費される時間の複雑さは、O(logn)に削減できます。二分探索については、アルゴリズムの部分で後で説明します。
3.2挿入操作
バイナリ検索ツリーで挿入操作を実行することも非常に簡単です。ルートノードから開始して、挿入するデータがルートノードのデータよりも大きく、ルートノードの右側の子ノードが空でない場合は、ルートノードの右側のサブツリーで挿入操作の実行を続けます。挿入は、空の子ノードが見つかるまで実行されます。
下の図に示すように、挿入するXの値が14の場合、Xとルートノードのサイズ関係を決定する必要があります。
- 14は16未満なので、左側のサブツリーに注目し、Xと13の関係を判断し続けます。
- 14は13より大きいため、正しいサブツリーに焦点を当て、Xと15の関係を判断し続けます。
- 14は15未満なので、左側のサブツリーに注目します。
このとき、左側のサブツリーは空であるため、15個のノードの左側のポインターとノードXの関係がポインターを介して直接確立され、挿入アクションが完了します。
バイナリ検索ツリーにデータを挿入する時間の複雑さはO(logn)です。しかし、これは通常の二分木よりも複雑であることを意味しません。その理由は、ここでの時間の複雑さは、データをトラバースして検索位置を見つけることに費やされ、実際に挿入アクションを実行する時間の複雑さは依然としてO(1)であるためです。
3.3削除操作
特定のノードを削除した後のツリーは、バイナリ検索ツリーのプロパティを満たす必要があるため、バイナリ検索ツリーの削除操作はより複雑になります。ディスカッションは、次の3つの状況に分けられます。
(1)削除するノードがリーフノードの場合は、直接削除し、その親ノードポインターをnullにポイントします。
(2)削除するノードに子ノードが1つしかない場合は、その親ノードが指す子ノードのポインターをその子ノードのポインターに置き換えます。
(3)削除するノードに2つのサブノードがある場合、実行可能な操作方法は2つあります。
- 1つ目は、このノードの左側のサブツリーで最大のノードを見つけ、削除するノードを置き換えることです。
- 2つ目は、このノードの右側のサブツリーで最小のノードを見つけ、削除するノードを置き換えることです。
この記事は「データ構造とアルゴリズムの再学習」を学習するための私の学習ノートです。学習とコミュニケーションにのみ使用されます。共有されるため、商業的に転載しないでください!記事の画像参照:https://kaiwu.lagou.com/course/courseInfo.htm?courseId=185#/detail/pc?id=3347