AVLツリーは、バランスの取れたツリーの非常に古典的な例です。挿入と削除の過程で通常の検索ツリーは、ツリーの一方の端子の高さがツリーのもう一方の側よりもはるかに高い状況、つまり、リンクリストへのいわゆる縮退(検索は1つのノードを見つけることです)を生成する可能性があるため、検索が減少します効果。
AVLバランスツリーは一連の回転を使用するため、左側のサブツリーと右側のサブツリーの高さはそれほど変わらないため(<= 1)、検索効率は良好なレベルに維持されます。
方法は、ノードを挿入した後に
親ノードの高さを更新して、親ノードの左右のサブツリー間の高さの差が2であるかどうかを判別することです。その場合、対応する回転操作(4タイプ)を実行して、左右のサブツリー間の高さの差を減らします。そうでない場合は、ルートノードまで(もちろん再帰的に実行されるまで)、親ノードの親の左右のサブツリー間の高さの差が2であるかどうかを判断します。
シングル左利き、シングル右利き、左右、右左は3つのノード間の順序を調整するためのもので、コードは次のとおりです。特定の外観は描画されません。これらのポイントの順序を調整する過程で、検索ツリーのサイズの順序はまだ残っていることがわかります。通常配置。
つまり、順序どおりのトラバーサルの結果は、小さいものから大きいものへとソートされたままです。
avl_tree.h
#ifndef _Avltree_h
#define _Avltree_h
#define Max(a,b) ((a)>(b)?(a):(b))
struct AvlNode;
typedef struct AvlNode*Position;
typedef struct AvlNode*AvlTree;
typedef int ElementType ;
Position Find(ElementType X,AvlTree T);
Position FindMin(AvlTree T);
Position FindMax(AvlTree T);
AvlTree MakeEmpty(AvlTree T);
AvlTree Insert(ElementType X,AvlTree T);
void InOrder_2(AvlTree T,int depth);
int PostOrder_2(AvlTree T,int depth);
void PreOrder_2(AvlTree T,int depth);
void PreOrder(AvlTree T);
void InOrder(AvlTree T);
void PostOrder(AvlTree T);
struct AvlNode
{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
};
#endif
avl_tree.c
#include<stdio.h>
#include<stdlib.h>
#include"avl_tree.h"
static Position SingleRotateWithLeft(Position K2);
static Position DoubleRotateWithLeft(Position K3);
static Position SingleRotateWithRight(Position K2);
static Position DoubleRotateWithRight(Position K3);
static int Height (Position P);
static Position SingleRotateWithLeft(Position K2)
{
// puts("右旋");
Position K1;
K1=K2->Left;
K2->Left=K1->Right;
K1->Right=K2;
K2->Height=Max(Height(K2->Left),Height(K2->Right))+1;
K1->Height=Max(Height(K1->Left),K2->Height)+1;
return K1;
}
static Position DoubleRotateWithLeft(Position K3)
{
//puts("左右");
K3->Left=SingleRotateWithRight(K3->Left);
return SingleRotateWithLeft(K3);
}
static Position SingleRotateWithRight(Position K2)
{
/*
1
0 2 k2
4 k1
5
1
0 4 k1
2 5
*/
// puts("单左旋");
Position K1;
K1=K2->Right;
K2->Right=K1->Left;
K1->Left=K2;
K2->Height=Max(Height(K2->Left),Height(K2->Right))+1;
K1->Height=Max(Height(K1->Left),K2->Height)+1;
return K1;
}
static Position DoubleRotateWithRight(Position K3)
{
// puts("右左");
/*
//120534
1 1 1
0 2 k3 --》 0 2 --》 0 3
5 k3->right 3 2 5
3 5
*/
K3->Right=SingleRotateWithLeft(K3->Right);
return SingleRotateWithRight(K3);
}
void InOrder(AvlTree T)
{
if(T!=NULL){
InOrder(T->Left);
printf("%d\n",T->Element);
InOrder(T->Right);
}
}
void PostOrder(AvlTree T)
{
if(T!=NULL){
PostOrder(T->Left);
PostOrder(T->Right);
printf("%d\n",T->Element);
}
}
void PreOrder_2(AvlTree T,int depth)
{
if(T!=NULL){
for (int i = 0; i < depth; i++)
printf("\t");
printf("%d\n",T->Element);
PreOrder_2(T->Left,depth+1);
PreOrder_2(T->Right,depth+1);
}
}
void InOrder_2(AvlTree T,int depth)
{
if(T!=NULL){
InOrder_2(T->Left,depth+1);
for (int i = 0; i < depth; i++)
printf("\t");
printf("%d\n",T->Element);
InOrder_2(T->Right,depth+1);
}
}
int PostOrder_2(AvlTree T,int depth)
{
int retSize;
int retSize2;
if(T!=NULL){
retSize=PostOrder_2(T->Left,depth+1);
retSize2=PostOrder_2(T->Right,depth+1);
for (int i = 0; i < depth; i++)
printf("\t");
printf("%d(%d)\n",T->Element,T->Element+retSize+retSize2);
return retSize2+retSize+T->Element;
}else{
return 0;
}
}
void PreOrder(AvlTree T)
{
if(T!=NULL){
printf("%d\n",T->Element);
PreOrder(T->Left);
PreOrder(T->Right);
}
}
AvlTree MakeEmpty(AvlTree T)
{
//后序
if(T!=NULL){
MakeEmpty(T->Left);
MakeEmpty(T->Right);
free(T);
}
return NULL;
}
static int Height (Position P)
{
if(P==NULL)
return -1;
else
return P->Height;
}
Position Find(ElementType X,AvlTree T)
{
if(T==NULL)
return NULL;
if(X<T->Element)
return Find(X,T->Left);
else if(X>T->Element)
return Find(X,T->Right);
else
return T;
}
AvlTree Insert(ElementType X,AvlTree T)
{
if(T==NULL){
T=malloc(sizeof(struct AvlNode));
if(T==NULL){
fprintf(stderr,"Out of space");
return NULL;
}
else{
T->Element=X;
T->Height=0;
T->Left=T->Right=NULL;
}
//放在左(递归)
}
else if(X<T->Element){
// printf("%d<%d\n",X,T->Element);
T->Left=Insert(X,T->Left);
if(Height(T->Left)-Height(T->Right)==2)
//左左->单右旋
if(X<T->Left->Element)
T=SingleRotateWithLeft(T);
//左右->双右左旋
else
T=DoubleRotateWithLeft(T);
}
else if(X>T->Element){
T->Right=Insert(X,T->Right);
if(Height(T->Right)-Height(T->Left)==2)
if(X>T->Right->Element){
T=SingleRotateWithRight(T);
}
else {
T=DoubleRotateWithRight(T);
}
}
T->Height=Max(Height(T->Left),Height(T->Right))+1;
return T;
}
自分で作成した新しいトラバーサル関数は、プレオーダートラバーサル(PreOrder_2)、インオーダートラバーサル(INOrder_2)、およびポストオーダートラバーサル(PostOrder_2)の効果を出力できます。
- 事前注文トラバーサル:中央左と右(メンテナンスの深さ、子ノードは親ノードから継承)
- ミドルオーダートラバーサル:左、中、右から小から大へ
- 注文後のトラバーサル:左右(高さを維持し、親ノードは子ノードから取得されます)(ディレクトリのサイズをカウントするために使用でき、子ファイルのサイズがディレクトリに追加および追加されます)ここでは、代わりにノードの重みを表すために数値を直接使用しますファイルサイズと親ノードへのマージ。
(中段の印刷は木のようだと思います)
#include<stdio.h>
#include<stdlib.h>
#include"avl_tree.h"
int main()
{
AvlTree T=NULL;
// int X=1;
// T=Insert(X,T);
// X=0;
// T=Insert(X,T);
// X=2;
// T=Insert(X,T);
// X=5;
// T=Insert(X,T);
// X=3;
// T=Insert(X,T);
/*
1 1 1
0 2 0 2 0 3
5 3 2 5
3 5
*/
for (int i = 0; i < 10; i++)
{
T=Insert((i+1),T);
}
// if(Find(3,T)->Right){
// printf("r%d\n",Find(3,T)->Right->Element);
// }
// if(Find(3,T)->Left){
// printf("l%d\n",Find(3,T)->Left->Element);
// }
puts("先");
PreOrder(T);
puts("中");
InOrder(T);
puts("后");
PostOrder(T);
// puts("后2");
// PostOrder_2(T,0);
// PreOrder_2(T,0);
InOrder_2(T,0);
MakeEmpty(T);
}
先
4
2
1
3
8
6
5
7
9
10
--------------------------
中
1
2
3
4
5
6
7
8
9
10
--------------------------
后
1
3
2
5
7
6
10
9
8
4
--------------------------
先
4
2
1
3
8
6
5
7
9
10
--------------------------
中
1
2
3
4
5
6
7
8
9
10
--------------------------
后
1(1)
3(3)
2(6)
5(5)
7(7)
6(18)
10(10)
9(19)
8(45)
4(55)
--------------------------
この記事では、AVLの効果を説明するために、あまり説明していません。
元々、123456789 10を挿入すると、ツリーがどこまで成長するかを想像できます。
ローテーションの4つの機能については、自分で理解したほうがいいですし、他の人のブログを読むよりもずっと便利です。
当時これらのアルゴリズムを発明したTucaoの人々は、私たちよりもはるかに優れています。
「データ構造とアルゴリズムC言語の説明」を参照してください。