基本的なアルゴリズムについての簡単な説明: AVL ツリーとスプレイ ツリー (パート 3)

基本的なアルゴリズムについての簡単な説明: AVL ツリーとスプレイ ツリー (パート 3)

順序

上記に引き続き、このトピックについて話し続けましょう。

バランス二分木: AVL 木 (1962)

上記では、単一の回転のみを実装しましたが、実際にはバランスをとるために、多くの二重回転操作が必要になります。

まずは二重回転後の画像を見てみましょう。右側の画像の方がクエリしやすいのは明らかです。

全体のプロセス

それではコードを練習してみましょう。

  1. # <stdio.h>をインクルードする
  2. # <stdlib.h>をインクルードする
  3.  
  4. #define max(a,b) (((a) > (b)) ? (a) : (b))
  5.  
  6. typedef 構造体 AvlNode{
  7. 整数データ;
  8. 構造体 AvlNode *left_child、*right_child;
  9. }AvlNode;
  10.  
  11. AvlNode *ルート;
  12.  
  13. /* 回転を開始します */  
  14. AvlNode *rotate_LL(AvlNode *親){
  15. AvlNode *child = parent->left_child;
  16. 親->左の子 = 子->右の子;
  17. 子->right_child = 親;
  18. を返します
  19. }
  20.  
  21. AvlNode *rotate_RR(AvlNode *親){
  22. AvlNode *child = parent->right_child;
  23. 親->右の子 = 子->左の子;
  24. 子->left_child = 親;
  25. を返します
  26. }
  27.  
  28. AvlNode *rotate_RL(AvlNode *親){
  29. AvlNode *child = parent->right_child;
  30. 親->right_child = rotate_LL(child);
  31. rotate_RR(親)を返します
  32. }
  33.  
  34. AvlNode *rotate_LR(AvlNode *親){
  35. AvlNode *child = parent->left_child;
  36. 親->left_child = rotate_RR(child);
  37. rotate_LL(親)を返します
  38. }
  39. /* 回転終了 */  
  40.  
  41. int get_height(AvlNode *ノード){
  42. 整数高さ = 0;
  43. if (ノード != NULL){
  44. 高さ = 1 + max(get_height(node->left_child), get_height(node->right_child));
  45. }
  46. 高さを返します
  47. }
  48.  
  49. int get_balance(AvlNode *ノード){
  50. if (node ​​== NULL) 0を返します
  51. get_height(node->left_child) - get_height(node->right_child) を返します
  52. }
  53.  
  54. /* バランスのとれた二分木 */  
  55. AvlNode *balance_tree(AvlNode **node){
  56. int height_diff = get_balance(*node); /* バランス係数は -1 から 1 の間です*/  
  57.  
  58. 高さの差が1より大きい場合
  59. get_balance((*node)->left_child) > 0の場合{
  60. *ノード = rotate_LL(*ノード);
  61. }それ以外{
  62. *ノード = rotate_LR(*ノード);
  63. }
  64. }それ以外 高さの差が -1 未満の場合{
  65. get_balance((*node)->right_child) < 0)の場合{
  66. *ノード = rotate_RR(*ノード);
  67. }それ以外{
  68. *ノード = rotate_RL(*ノード);
  69. }
  70. }
  71. *ノードを返します
  72. }
  73.  
  74. AvlNode *avl_add(AvlNode **ルート、intキー){
  75. (*root == NULL)の場合{
  76. *ルート = (AvlNode *)malloc(sizeof(AvlNode));
  77. (*root == NULL)の場合{
  78. printf( "メモリの割り当てに失敗しました!\n" );
  79. 終了(-1);
  80. }
  81.  
  82. (*root)->データ = キー;
  83. (*root)->left_child = (*root)->right_child = NULL;
  84. }それ以外  if (キー < (*ルート)->データ){
  85. (*root)->left_child = avl_add(&((*root)->left_child), キー);
  86. //balance_tree(ルート);  
  87. }それ以外  if (キー > (*ルート)->データ){
  88. (*root)->right_child = avl_add(&((*root)->right_child), キー);
  89. //balance_tree(ルート);  
  90. }それ以外{
  91. printf( "%d のコピーに失敗しました!\n" , key);
  92. 終了(-1);
  93. }
  94.  
  95. *rootを返します
  96. }
  97.  
  98. AvlNode *avl_print(AvlNode *ノード){
  99. if (node ​​== NULL)はNULLを返します
  100.  
  101. printf( "%d->" , ノード->データ);
  102.  
  103. avl_print(ノード->left_child);
  104. avl_print(ノード->right_child);
  105. ノードを返します
  106. }
  107.  
  108. int main(){
  109. avl_add(&root, 24);
  110. avl_add(&root, 17);
  111. avl_add(&root, 40);
  112. avl_add(&root, 8);
  113. avl_add(&root, 22);
  114. avl_add(&root, 18);
  115. avl_add(&root, 23);
  116.  
  117. printf( "バイナリツリーを印刷\n" );
  118. avl_print(ルート);
  119. printf( "\n" );
  120.  
  121. バランスツリー(&root);
  122. printf( "バイナリツリーを印刷\n" );
  123. avl_print(ルート);
  124. printf( "\n" );
  125. 0を返します
  126. }

ストレッチツリーを見てみましょう!

スプレイツリー(1985)

スプレーツリーの基本原理:

lighttpd-1.4.31.tar.gzからいくつかのコードを抽出して説明しました

具体的なコード練習を見たい方は、以下の場所で視聴できます。

私のコード構造:

コードセクション

  1. /*~ splaytree.h~*/  
  2. typedef 構造体 tree_node{
  3. 構造体 tree_node *左、*右;
  4. int key; /* キーワード */  
  5. int size; /* ノード数 */  
  6. void *データ;
  7. } splay_tree;
  8. /*今はこの2つのメソッドだけを書いています*/  
  9. splay_tree * splaytree_insert(splay_tree *t, int キー、void *データ);
  10. splay_tree * splaytree_splay(splay_tree *t, int キー);
  11. #define node_size(x) (((x)==NULL) ? 0 : ((x)->size))

これを詳しく説明する必要はありません。コメントとメソッド名を見れば、何をするのかがわかります。

  1. /* splaytree.c */  
  2. #include "splaytree.h"  
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <assert.h>  
  6. #define compare(i,j) ((i) - (j))  
  7. splay_tree * splaytree_insert(splay_tree *t, intキー、 void *データ){
  8. splay_tree *新規;
  9.  
  10. t != NULLの場合{
  11. t = splaytree_splay(t, キー);
  12. if (compare(key, t->key) == 0){ /* ノードはすでに存在します */  
  13. tを返します
  14. }
  15. }
  16. 新しい= (splay_tree *) malloc ( sizeof (splay_tree));
  17. assert(新しい);
  18. t == NULLの場合{
  19. new- >left = new- >right = NULL;
  20. }それ以外  (キー、t->キー)<0を比較する場合
  21. new- >left = t->left;
  22. 新しい->right = t;
  23. t->左 = NULL;
  24. t->size = 1 + node_size(t->right);
  25. }それ以外{
  26. new- >right = t->right;
  27. 新しい- >left = t;
  28. t->右 = NULL;
  29. t->size = 1 + node_size(t->left);
  30. }
  31. 新しい->key = key;
  32. 新しい- >data = データ;
  33. new- >size = 1 + node_size( new- >left) + node_size( new- >right);
  34. 戻る 新しい;
  35. }
  36. splay_tree * splaytree_splay(splay_tree *t, intキー){
  37. splay_tree N, *l, *r, *child; /* 一時変数は *t を組み立てるために使用されます */  
  38. int cmp、l_size、r_size;
  39. t == NULL の場合はtを返します
  40. N.左 = N.右 = NULL;
  41. l = r = &N;
  42. l_size = r_size = 0;
  43. のために(;;) {
  44. cmp = compare(キー、t->キー);
  45.  
  46. (cmp < 0)の場合{
  47. if (t->left == NULL) break ;
  48. 比較(キー、t->left->key) < 0) {
  49. child = t->left; /* 右に回転 */  
  50. t->左 = 子->右;
  51. 子->右 = t;
  52. t->size = 1 + node_size(t->left) + node_size(t->right);
  53. t = 子供;
  54. if (t->left == NULL) break ;
  55. }
  56. r->left = t; /* 右チェーン */  
  57. r = t;
  58. t = t->左;
  59. r_size += 1 + node_size(r->right);
  60. }それ以外  (cmp > 0)の場合{
  61. if (t->right == NULL) break ;
  62.  
  63. 比較(キー、t->right->key) > 0) {
  64. 子 = t->right;
  65. t->右 = 子->左;
  66. 子->左 = t;
  67. t->size = 1 + node_size(t->left) + node_size(t->right);
  68. t = 子供;
  69. if (t->right == NULL) break ;
  70. }
  71. l->右 = t;
  72. t = 0;
  73. t = t->右;
  74. l_size += 1 + node_size(l->left);
  75. }それ以外{
  76. 壊す;
  77. }
  78. }
  79. l_size += node_size(t->left);
  80. r_size += node_size(t->right);
  81. t->サイズ = 1 + l_size + r_size;
  82.  
  83. l->右 = r->左 = NULL;
  84.  
  85. /* サイズデータをチェック */  
  86. /*右の子の左のノードはカウントされません*/  
  87. ( child = N.right; child != NULL; child = child->right){
  88. 子->サイズ = l_size;
  89. l_size -= 1 + node_size(child->left);
  90. }
  91. ( child = N.left; child != NULL; child = child->left){
  92. 子->サイズ = r_size;
  93. r_size -= 1 +node_size(child->right);
  94. }
  95. /* アセンブリデータ */  
  96. l->右 = t->左;
  97. r->左 = t->右;
  98. t->左 = 北右;
  99. t->右 = N.左;
  100. tを返します
  101. }

上記のコードを見ると、誰もが混乱するのではないでしょうか?

詳しく説明しましょう。

>> コアアルゴリズム splaytree_splay() メソッドに注目しましょう。

この if が意味を成すのであれば、他の else if も理解しやすくなります。

  1. (cmp < 0)の場合{
  2. if (t->left == NULL) break ;
  3.  
  4. (キー、t->left->key)<0を比較する場合
  5. child = t->left; /* 右に回転 */  
  6. t->左 = 子->右;
  7. 子->右 = t;
  8. t->size = 1 + node_size(t->left) + node_size(t->right);
  9. t = 子供;
  10.  
  11. if (t->left == NULL) break ;
  12. }
  13. r->left = t; /* 右チェーン */  
  14. r = t;
  15. t = t->左;
  16. r_size += 1 + node_size(r->right);
  17. }

これは右利きのプロセスです。

子 = t->左

t->left = child->right; child->right = t;

最後に: t = 子

これは右連鎖プロセスです。

r->左 = t;r=t;

t = t->左

3>メイン.c

  1. # <stdio.h>をインクルードする
  2. #含む  「splaytree.h」  
  3. splay_tree * splaytree_print(splay_tree *t){
  4. (t != NULL)の場合{
  5. printf( "t->data:%d\t t->key:%d t->size:%d\n" , *((int *)t->data), t->key, t->size);
  6. splaytree_print(t->left);
  7. splaytree_print(t->right);
  8. }
  9. }
  10. int main(){
  11. splay_tree *ルート;
  12. ルート = NULL;
  13. 整数データ1 = 1000;
  14. ルート = splaytree_insert(ルート、20、&data1);
  15. 整数データ2 = 200;
  16. ルート = splaytree_insert(ルート、5、&data2);
  17. 整数データ3 = 300;
  18. ルート = splaytree_insert(ルート、3、&data3);
  19. 整数データ4 = 100;
  20. ルート = splaytree_insert(ルート、10、&data4);
  21. printf( "結果を印刷*************\n" );
  22. splaytree_print(ルート);
  23.  
  24.  
  25. //ここにはデータクエリプロセスがあるはずですが、記述しませんでした。呼び出すときは、splay メソッドを呼び出すことに注意してください。  
  26. //すると、ホット データが自然に最上位に表示されます。  
  27. printf( "クエリメソッドでこのストレッチ関数を呼び出した後、結果を出力します **************\n" );
  28. ルート = splaytree_splay(ルート、3);
  29. splaytree_print(ルート);
  30. 0を返します
  31. }

ここではスプレイ ツリーをクエリするプロセスについては説明していませんが、クエリ プロセス中にこのコア メソッドが呼び出される限り、ホット データは自然に最上位に移動することを強調しておきます。

プロセスを見てみましょう

オリジナルリンク: http://www.cnblogs.com/baochuan/archive/2012/10/16/2716641.html

【編集者のおすすめ】

  1. 大規模ウェブサイトバックエンド構築実習
  2. 画像ストレージアーキテクチャの学習:キャッシュ、建築家の美しい愛人
  3. 大規模ウェブサイトのアルゴリズムとアーキテクチャについての簡単な説明(パート 2)
  4. 大規模ウェブサイトのアルゴリズムとアーキテクチャに関する簡単な説明
  5. エンタープライズ アプリケーション アーキテクチャ モデル - 作業単位モデル

<<:  Google: より多くのデータはより優れたアルゴリズムに勝ります!

>>:  シャッフルアルゴリズムの2つの実装の比較

ブログ    

推薦する

ジェネレーティブ AI: 誇大宣伝以上の価値を生み出す 3 つの重要な要素

最近、ガートナーは、生成型人工知能 (GenAI) を新興技術の誇大宣伝サイクルにおける「過大な期待...

AI トレーニングを容易にするために、分散を通じてクラウドで弾力的なスループットを実現するにはどうすればよいでしょうか?

翻訳者 | 李睿レビュー | Chonglou人工知能は現在、定量的研究などの分野におけるソフトウェ...

自動運転と安全性の「距離」

4月15日、2021年上海モーターショー前夜、ファーウェイは自動運転システムADSのプロモーション...

NeRF を放棄し始めていますか?ガウススプラッティングが自動運転のシナリオで人気があるのはなぜですか?

この記事は、Heart of Autonomous Driving の公開アカウントから許可を得て転...

ロボットが製造業にもたらした変化は実に目覚ましいものがあります。

知能ロボットの誕生は、国内の多くの産業に新たな力をもたらしました。ロボットの導入により、サービス業は...

ニューラル ネットワーク アルゴリズムを使用した C# での手書き数字認識

デモをダウンロード - 2.77 MB (元のアドレス)手書き文字認識.zipソースコードをダウンロ...

同社はコストバランスに苦戦しており、AI部門で猛烈な採用を行い、他の部門では人員削減を行っている。

業界の専門家は、テクノロジー企業がAIへの投資を優先し、採用を急ぐため、他の分野での人員削減は202...

人工知能: スマートシティを支える頭脳

[[347829]]私たちが知っているかどうかに関わらず、人工知能 (AI) はすでに私たちの生活の...

最新の3D GANは3次元の幾何学データを生成できます!モデル速度が7倍に向上

[[441513]]この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI...

MiniGPT-4: 高度な大規模言語モデルを使用した AI 視覚言語理解の向上

1. プロジェクトの背景と動機今年初め、OPEN AI の GPT-4 は前例のないマルチモーダル機...

米国でレベル4自動運転システムの一部がリコールされた。Pony.aiはどんなミスを犯したのか?

自動運転車が交通事故に巻き込まれるのは今回が初めてではない。しかし、今回のPony.aiによるL4...

マイクロソフト、AIツール「コパイロット」があなたの仕事を奪うことはないと改めて主張

Responsible AI チームを発表した際、Microsoft の幹部は、Copilot は仕...

...

Pytorchの核心部分である自動微分化を突破! !

こんにちは、Xiaozhuangです! PyTorch での自動微分演算に関して、この論文では Py...