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つのフィールドのみが定義されています:id
、name
。しかし、余分なデータベース出ていた5
分野、lft
すなわち:rgt
、level
、tree_id
、parent_id
、。
これらの追加フィールドは、ツリーの構造と階層を定義するために使用されます。各分野の役割を分析してみましょう。
-
tree_id
:ツリーid
。データベース内の多数のツリーの中から特定のツリーを区別するために使用されます。 -
level
:標準ツリーには、高さ、深さ、レベルがあります。ルートノードのレベルは1
、であり、子ノードのレベルは親ノードのレベルに1
。を加えたものです。 -
parent_id
:親id
、id
ノードの親、ルートノードには親がないため、値は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
すべての子ノードのノードを検索します1
。tree_id
等しい1
とlevel
等しい2
。
ノードのパスを見つける
参照点として、9
すべての上位パスのノードを見つけます9
。値が左14
より小さく、右が15
すべてのノードの値よりも大きい場合、ノードは「。9.1- 的路径。结果是:
> 7-> 8-> 9」です。
3.MPTTバランスアルゴリズム
MPTT
すばやく移動するが、他の操作は非常に遅くなるためMPTT
、クエリ以外の他の操作を回避するように使用してください。
では、クエリ操作以外の操作が非常に遅いのはなぜですか?
これは、ノードの挿入、更新(移動)、および削除によってツリーのバランスが崩れるためです。したがって、これらの操作を行うときは、新しいバランスを実現するために数値を調整する必要があります。
追加
新しいノード操作を例にとると、アルゴリズムは次のステップに分解できます。
-
存在しないツリーに新しいノードを追加する場合は、新しいツリーを作成する必要があります。そして、そうではありません
parent_id
、それは、parent_id
あるNULL
、level
それは1
、tree_id
既存のツリーに合わせて最大でtree_id
プラス1
。 -
既存のツリーに新しいノードを追加する場合。つまり、これ
parent_id
は親ノードid
、level
親ノードlevel
プラス1
、tree_id
および一貫性のある親です。 -
バランスが崩れている他のノードの左の値を修復します。
parent_id
値に追加されたすべてのノードの左右の値よりも大きい2
。 -
バランスが崩れている他のノードの正しい値を修復します。以下またはそれに等しい
parent_id
正しい値を加えた値のすべてのノードの権利2
。
削除
ノードを削除した後、左の値と右の値が逆になる、つまり減算することを除いて、増加に似ています2
。
アップデート(モバイル)
更新(移動)とは、実際には古いノードを削除し、新しいノードを追加することです。特定のアルゴリズムについては、上記の例を参照してください。
3.標準ツリーと事前にソートされたトラバーサルツリーの長所と短所の比較
-
标准树
:追加や削除が多いシーンに適しており、毎回1つのデータしか変更する必要がありません。クエリに関しては、分類レベルが高くなると、隣接テーブルの再帰クエリの効率が徐々に低下します。 -
预排序遍历树
:クエリ操作が多いシナリオに適用できます。分類レベルの増加によるクエリ効率への影響はありません。ただし、データの増加に伴い、データを追加または削除するたびに、影響を受ける複数のデータを同時に操作する必要があります。実行効率は徐々に低下します。
完璧なアルゴリズムはありません。実際の開発プロセスで選択するストレージ構造とアルゴリズムは、特定のアプリケーションシナリオに応じて選択する必要があります。