コンピュータサイエンスでは、AVLツリーは発明された最初の自己平衡二分探索木です。AVLツリー内の任意のノードの2つのサブツリーの高さの最大差は1であるため、高さバランスツリーとも呼ばれます。追加と削除では、ツリーのバランスを取り直すために1つ以上のツリーローテーションが必要になる場合があります。
特徴
AVLツリーは本質的にバイナリ検索ツリーであり、次の特徴があります。
- 二分探索木です
- 各ノードの左右のサブツリーの高さの差の絶対値(バランス係数)は最大1です。
次の図はAVLツリーであり、各ノードはバランス係数でマークされてい
ます。ノード4の左側のサブツリーの高さは0であり、右側のサブツリーは存在しないため、バランス係数は0-(-1)=です。 1;すべての葉ノードには左右のサブツリーがないため、バランス係数は0です。
上記のAVLツリーの場合、0が挿入されると、この時点でのバイナリツリーは次のようになります。
ノード4のバランス係数は2であり、AVLツリーのバランスを崩します。では、AVLのバランスをどのように復元するのでしょうか。左回転と右回転の2回の操作で完了します。
スピン
AVL木の回転は、左右タイプ、左右タイプ、左右タイプ、左右タイプの4つのケースに分けられます。
左左
グラフ内のノードが左に偏っている状況では、左左型と呼ばれますが、このとき、ノード4で右回転操作を行い、バランスを取り戻します。
右回転:2つのノードを時計回りに回転して、親ノード4がそれ自体の左側の子ノード1に置き換えられ、ノード4がノード1の右側の子ノードになります。
例を示し
ます。ノード6のバランス係数は2で、この時点では左左タイプであり、右利きの操作が実行されます。このとき、ノード4の右の子がノード6の左の子になります。
右右タイプ
写真は左手操作用の右から右へのタイプを示しています
右左型
図は右左タイプです。まず、ノード6で右利きの操作を実行し、次にノード4で左利きの操作を実行します。
左右
図は左右のタイプを示しています。まず、ノード2で左側の操作を実行し、ノード8で右側の操作を実行します。
コード
public class Node {
int data;
Node lchild;
Node rchild;
int height;
public Node(int data) {
this.data = data;
}
}
public class AVLTree {
private Node root;
// 记录节点的高度
private int height(Node node) {
if (node == null) {
return -1;
} else {
return node.height;
}
}
// 右右型 左旋
private Node L_Rotate(Node node) {
Node temp;
// 进行旋转
temp = node.rchild;
node.rchild = temp.lchild;
temp.lchild = node;
// 重新计算高度
node.height = Math.max(height(node.lchild), height(node.rchild)) + 1;
temp.height = Math.max(height(temp.lchild), height(temp.rchild)) + 1;
return temp;
}
// 左左型 右旋
private Node R_Rotate(Node node) {
Node temp;
// 进行旋转
temp = node.lchild;
node.lchild = temp.rchild;
temp.rchild = node;
// 重新计算高度
node.height = Math.max(height(node.lchild), height(node.rchild)) + 1;
temp.height = Math.max(height(temp.lchild), height(temp.rchild)) + 1;
return temp;
}
// 左右型 先进行左旋在进行右旋
private Node L_R_Rotate(Node node) {
// 对左子节点进行左旋
node.lchild = L_Rotate(node.lchild);
// 对节点进行右旋
return R_Rotate(node);
}
// 右左型 先进性右旋在进行左旋
private Node R_L_Rotate(Node node) {
// 对右子节点进行右旋
node.rchild = R_Rotate(node.rchild);
// 对节点进行左旋
return L_Rotate(node);
}
public void insert(int data) {
root = insert(data, root);
}
// 插入操作
private Node insert(int data, Node node) {
if (node == null) {
node = new Node(data);
} else if (data < node.data) {
// 向左子节点递归插入
node.lchild = insert(data, node.lchild);
// 如果左子节点的高度比右子节点的高度大2 则进行旋转调整
if (height(node.lchild) - height(node.rchild) == 2){
if (data < node.lchild.data) {
// 左左型
node = R_Rotate(node);
} else {
// 左右型
node = R_L_Rotate(node);
}
}
} else if (data > node.data) {
// 向右子节点递归插入
node.rchild = insert(data, node.rchild);
// 如果右子节点的高度比左子节点的高度大2 则进行旋转调整
if (height(node.rchild) - height(node.lchild) == 2) {
if (data > node.rchild.data) {
// 右右型
node = L_Rotate(node);
} else {
// 右左型
node = R_L_Rotate(node);
}
}
}
// 如果 data = node.data 这个节点在树上存在 什么也不做
node.height = Math.max(height(node.lchild), height(node.rchild)) + 1;
return node;
}
// 中序遍历AVL树
public void inOrder() {
inOrder(root);
}
private void inOrder(Node node) {
if (node != null) {
inOrder(node.lchild);
System.out.println(node.data);
inOrder(node.rchild);
}
}
}