データ構造とアルゴリズム:事前ソートトラバーサルツリーアルゴリズム

1.マルチレベル分類問題

実際の開発プロセスでは、マルチレベル分類の問題が頻繁に発生します。たとえば、ナビゲーションバー、メニュー、製品カテゴリ、マルチレベルリンケージ、ディクショナリテーブルなどのマルチレベル分類の問題。次に、pidフィールド関連のデータを追加できます。これは、本質的には実際にはツリーです。ツリーは、マルチレベル分類のサブカテゴリクエリを非常にうまく解決できます。

ここに画像の説明を挿入します

しかし、この方法には致命的な問題があります。クエリの効率が低すぎます。

プログラムで子ノードにクエリを実行するときは、最初にルートノードから再帰クエリを実行する必要があります。時間計算量はO(n)です。

では、ツリーのクエリ効率を向上させる方法はありますか?答えはイエスです!二分木、赤黒木、ヒープなど、多くのツリーが標準ツリーで改善されています。しかし、これは重要ではありません预排序遍历树算法(MPTT)今日私が共有したいのはです。

MPTTマルチレベルのリレーショナルデータのクエリ効率の問題を解決するためだけに、その時間計算量は定数と同じくらい効率的である可能性がありますO(1)信じられないことではありませんか?事前ソートトラバーサルツリーアルゴリズムを一緒に学び、それがどのように実装されているかを見てみましょう。

次に、ソート前のトラバーサルツリー

ソート前のトラバーサルツリーアルゴリズムのフルネームは次のとおりです。Modified Preorder Tree Traversal略語MPTT

1.ORMマッピング

class Tree(Base, BaseNestedSets):

    __tablename__ = 'tree'

    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(8), nullable=True, default=None)

    def __repr__(self):
        return f'<Tree(id={self.id}, name={self.name})>'

2.MPTT分析

上記のコードでは、2つのフィールドのみが定義されています:idnameしかし、余分なデータベース出ていた5分野、lftすなわち:rgtleveltree_idparent_id、。

これらの追加フィールドは、ツリーの構造と階層を定義するために使用されます。各分野の役割を分析してみましょう。

  • tree_id:ツリーid。データベース内の多数のツリーの中から特定のツリーを区別するために使用されます。

  • level:標準ツリーには、高さ、深さ、レベルがあります。ルートノードのレベルは1、であり子ノードのレベルは親ノードのレベルに1。を加えたものです。

  • parent_id:親ididノードの親、ルートノードには親がないため、値はNULLです。

  • lft:ノードの左側の値。

  • rgt:ノードの正しい値。

左右の値ノードはMPTTこのアルゴリズムの中核であり、特に賢い場所であり、複雑さはツリー走査時間に削減されますO(1)次に、左右の値がツリーをどのように横断するかを分析することに焦点を当てます。

標準的なツリー構造:

ここに画像の説明を挿入します

データベースデータの対応:

ここに画像の説明を挿入します

データ階層:

- 【1】
- - 【2】
- - - 【3】
- - 【4】
- - - 【5】
- - - 【6】
- - 【7】
- - - 【8】
- - - - 【9】
- - - 【10】
- - - - 【11】

ツリー全体をトラバースします

ツリー全体をトラバースすることは、単に見つける必要があるtree_id等しい1に条件を

ノードのすべての子孫を検索します

参照点として、ノードの4すべての子孫のノードを検索します4値は左より大きく6、右の値は11すべての子孫ノードより小さく、ノードは4すべての子孫ノードです。

ノードの下のすべての子ノードを検索します

参照点としての1すべての子ノードのノードを検索します1tree_id等しい1level等しい2

ノードのパスを見つける

参照点として、9すべての上位パスのノードを見つけます9値が左14より小さく、右が15すべてのノードの値よりも大きい場合、ノードは「。9.1- 的路径。结果是:> 7-> 8-> 9」です。

3.MPTTバランスアルゴリズム

MPTTすばやく移動するが、他の操作は非常に遅くなるためMPTT、クエリ以外の他の操作を回避するように使用してください。

ここに画像の説明を挿入します

では、クエリ操作以外の操作が非常に遅いのはなぜですか?

これは、ノードの挿入、更新(移動)、および削除によってツリーのバランスが崩れるためです。したがって、これらの操作を行うときは、新しいバランスを実現するために数値を調整する必要があります。

追加

新しいノード操作を例にとると、アルゴリズムは次のステップに分解できます。

  • 存在しないツリーに新しいノードを追加する場合は、新しいツリーを作成する必要があります。そして、そうではありませんparent_id、それは、parent_idあるNULLlevelそれは1tree_id既存のツリーに合わせて最大でtree_idプラス1

  • 既存のツリーに新しいノードを追加する場合。つまり、これparent_idは親ノードidlevel親ノードlevelプラス1tree_idおよび一貫性のある親です。

  • バランスが崩れている他のノードの左の値を修復します。parent_id値に追加されたすべてのノードの左右の値よりも大きい2

  • バランスが崩れている他のノードの正しい値を修復します。以下またはそれに等しいparent_id正しい値を加えた値のすべてのノードの権利2

削除

ノードを削除した後、左の値と右の値が逆になる、つまり減算することを除いて、増加に似ています2

アップデート(モバイル)

更新(移動)とは、実際には古いノードを削除し、新しいノードを追加することです。特定のアルゴリズムについては、上記の例を参照してください。

3.標準ツリーと事前にソートされたトラバーサルツリーの長所と短所の比較

  • 标准树:追加や削除が多いシーンに適しており、毎回1つのデータしか変更する必要がありません。クエリに関しては、分類レベルが高くなると、隣接テーブルの再帰クエリの効率が徐々に低下します。

  • 预排序遍历树:クエリ操作が多いシナリオに適用できます。分類レベルの増加によるクエリ効率への影響はありません。ただし、データの増加に伴い、データを追加または削除するたびに、影響を受ける複数のデータを同時に操作する必要があります。実行効率は徐々に低下します。

完璧なアルゴリズムはありません。実際の開発プロセスで選択するストレージ構造とアルゴリズムは、特定のアプリケーションシナリオに応じて選択する必要があります。

おすすめ

転載: blog.csdn.net/yilovexing/article/details/107066591