В сбалансированном AVL дереве, фактор баланса для узла T, может принимать одно из трёх значений:
Если после вставки или удаления узла, разница между высотами веток, в каком-либо из узлов стала больше 1, то требуется сбалансировать данный узел, повернув данный узел.
→
Здесь высота дерева B была больше высоты дерева C, что мы исправили с помощью поворота. Перед выполнением поворота следуется убедится что высота ветки 1 больше высоты ветки 2 (в узле B). Если это не так (например мы вставили узел в ветку 2 узла B), то это следует исправить с помощью предварительного поворота узла B (назовём это вложенный поворот).
| → |
|
Для исправления фаторов баланса в подобной ситуации, достаточно знать факторы баланса двух вершин перед поворотом, и исправить значения этих же вершин после поворота. Сами значения проще всего описать в табличной форме. Не важно, делаем мы при этом основной или вложенный поворот. Если мы делаем внешний поворот, то предполагается что внутрений уже выполнен. Так же предополагается что фактор баланса лежит в границах от -2 до +2, AVL дереве других факторов быть не может, даже во время временного дисбаланса.
|
|
struct node_st{ node_st *p1; // левая ветка node_st *p2; // правая ветка int value; // значение узла int b; // фактор баланса }; |
node_st *Rotate12(node_st *node) { static const int array[6][4]={ -1,-1,+1,+1, -1,+0,+1,+0, -1,+1,+2,+0, -2,-1,+0,+0, -2,-2,+0,+1, -2,+0,+1,-1 }; node_st *p1=node->p1; node_st *p12=p1->p2; p1->p2=node; node->p1=p12; for(int n=0;n<6;n++) if(array[n][0]==node->b && array[n][1]==p1->b) { p1->b=array[n][2]; node->b=array[n][3]; break; } return p1; } |
node_st *Rotate21(node_st *node) { static const int array[6][4]={ +1,-1,-2,+0, +1,+0,-1,+0, +1,+1,-1,-1, +2,+0,-1,+1, +2,+1,+0,+0, +2,+2,+0,-1 }; node_st *p2=node->p2; node_st *p21=p2->p1; p2->p1=node; node->p2=p21; for(int n=0;n<6;n++) if(array[n][0]==node->b && array[n][1]==p2->b) { p2->b=array[n][2]; node->b=array[n][3]; break; } return p2; } |
bool BalanceInsert(node_st **root) { node_st *node=*root; if(node->b>1) { if(node->p2->b<0) node->p2=Rotate12(node->p2); *root=Rotate21(node); return true; } if(node->b<-1) { if(node->p1->b>0) node->p1=Rotate21(node->p1); *root=Rotate12(node); return true; } return false; } |
node_st *tree_root=0; // корень дерева Insert(value,&tree_root); // вставка значения в дерево bool Insert(int value,node_st **root) { bool res=false; node_st *node=*root; if(!node) { *root=NewItem(value); return true; } if(value==node->value) return false; if(value<node->value) res=Insert(value,&node->p1) && !!--node->b; else res=Insert(value,&node->p2) && !!++node->b; if(BalanceInsert(root)) res=false; return res; } |
Мы удалили узел из ветки 3, и нам требуется поворот. В это ситуации, если баланс узла B равен нулю, то высота ветки A, после его балансировки не изменится. Для примера можете рассмотреть следующие упрощённые деревья (x - только что удалённая вершина).
0 0 0 / \ / \ / \ 1 x 1 x 1 x / \ / \ 2 3 2 2
bool BalanceRemove(node_st **root,bool res) { node_st *node=*root; if(node->b>1) { res=!!node->p2->b; if(node->p2->b<0) node->p2=Rotate12(node->p2); *root=Rotate21(node); } if(node->b<-1) { res=!!node->p1->b; if(node->p1->b>0) node->p1=Rotate21(node->p1); *root=Rotate12(node); } return res; } |
bool Remove(node_st **root,int value) { bool ok=false; node_st *node=*root; if(!node) return ok; if(node->value<value) { if(Remove(&node->p2,value) && !--node->b) ok=true; }else if(node->value>value) { if(Remove(&node->p1,value) && !++node->b) ok=true; }else { // нашли вершину кот. надо удалить if(!node->p2) { // если есть возможность вырезать сразу, вырезаем *root=node->p1;DelItem(node); return true; } ok=GetMin(&node->p2,root); // находим вершину, которую вставляем на место удалённой (*root)->b =node->b; // ставим нашу замену на место удалённой вершины (*root)->p1=node->p1; (*root)->p2=node->p2; DelItem(node); if(ok) ok=!--(*root)->b; } return BalanceRemove(root,ok); } |
bool tree::GetMin(node_st **root,node_st **res) { node_st *node=*root; if(node->p1) { if(GetMin(&node->p1,res) && !++node->b) return true; return BalanceRemove(root,false); } *res=node; *root=node->p2; return true; } |