Эта статья приняла участие"Проект "Звезда раскопок"", чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам
«Добро пожаловать для обсуждения в области комментариев, официальный представитель NuggetsПроект «Звезда раскопок»После мероприятия в комментариях будет разыграно 100 штук Наггетсов.Подробнее о лотерее читайте в статье о мероприятии».
Введение
Сбалансированное бинарное дерево поиска — это особый вид бинарного дерева поиска. Почему существует сбалансированное бинарное дерево поиска?
Рассмотрим частный случай бинарного дерева поиска.Если все узлы в бинарном дереве поиска являются правыми узлами, то бинарное дерево поиска вырождается в связанный список. В результате временная сложность поиска становится равной O(n), где n — количество узлов в бинарном дереве поиска.
Сбалансированное бинарное дерево поиска для решения этой возникающей проблемы заключается в ограничении высоты дерева, что уменьшит временную сложность O (logn).
Особенности компании АВЛ
Прежде чем обсуждать характеристики AVL, мы сначала вводим понятие, называемое коэффициентом баланса, который представляет собой разницу в высоте между левым и правым поддеревьями.
Если коэффициент баланса = 0, это означает, что это полностью сбалансированное бинарное дерево.
Если коэффициент баланса = 1, то дерево является сбалансированным бинарным деревом AVL.
То есть коэффициент баланса AVL не может быть больше 1.
Давайте посмотрим на пример AVL:
Подводя итог, AVL — это сначала бинарное дерево поиска, а затем бинарное сбалансированное дерево.
Строительство АВЛ
Учитывая особенности AVL, давайте посмотрим, как устроена AVL.
public class AVLTree {
//根节点
Node root;
class Node {
int data; //节点的数据
int height; //节点的高度
Node left;
Node right;
public Node(int data) {
this.data = data;
left = right = null;
}
}
Точно так же AVL также состоит из различных узлов, и каждый узел имеет несколько атрибутов данных, левый и правый.
Поскольку это бинарное сбалансированное дерево, сбалансированность узла также связана с высотой узла, поэтому нам также необходимо определить высоту как высоту узла.
Есть два вспомогательных метода, один из которых заключается в получении заданной высоты узла:
//获取给定节点的高度
int height(Node node) {
if (node == null)
return 0;
return node.height;
}
И получить баланс факторов:
//获取平衡因子
int getBalance(Node node) {
if (node == null)
return 0;
return height(node.left) - height(node.right);
}
АВЛ поиск
Метод поиска AVL такой же, как и у бинарного дерева поиска.
Давайте сначала рассмотрим на интуитивном примере, как искать узел 7 в AVL:
Основные этапы поиска:
- Начиная с корневого узла 15, сравните размер корневого узла и значение поиска.
- Если значение поиска меньше, чем значение узла, то рекурсивно искать левое дерево
- Если значение поиска больше, чем значение узла, то рекурсивно ищите правильное дерево.
- Если узел совпадает, он будет возвращен напрямую.
Соответствующий Java-код выглядит следующим образом:
//搜索方法,默认从根节点搜索
public Node search(int data){
return search(root,data);
}
//递归搜索节点
private Node search(Node node, int data)
{
// 如果节点匹配,则返回节点
if (node==null || node.data==data)
return node;
// 节点数据大于要搜索的数据,则继续搜索左边节点
if (node.data > data)
return search(node.left, data);
// 如果节点数据小于要搜素的数据,则继续搜索右边节点
return search(node.right, data);
}
Вставка АВЛ
Вставка AVL аналогична вставке BST, но после вставки дерево может быть не сбалансировано, поэтому нам нужно выполнить шаг перебалансировки.
Посмотрите интуитивно понятную анимацию:
Логика вставки следующая:
- Начиная с корневого узла, сравните данные узла с данными, которые нужно вставить.
- Если вставляемые данные меньше, чем данные узла, рекурсивная вставка левого поддерева
- Если вставляемые данные больше, чем данные узла, то рекурсивная вставка правого поддерева
- Если корневой узел пуст, вставьте текущие данные в качестве корневого узла.
После вставки данных нам нужно сделать ребалансировку.
Логика ребалансировки следующая:
- Найдите первый несбалансированный узел вверх от вставленного узла, который мы обозначаем как z
- Z вращение корень поддерева, чтобы получить сбалансированное дерево.
В зависимости от дерева с корнем z у нас есть четыре поворота:
- слева-налево:
Если это лево-левое дерево, то достаточно правого вращения.
Какие шаги, чтобы повернуть направо?
- Найдите левый узел y узла z
- Корневой узел как повернутый y
- z как правый узел y
- Правый узел y действует как левый узел z
- обновить высоту z
Соответствующий код выглядит следующим образом:
Node rightRotate(Node node) {
Node x = node.left;
Node y = x.right;
// 右旋
x.right = node;
node.left = y;
// 更新node和x的高度
node.height = max(height(node.left), height(node.right)) + 1;
x.height = max(height(x.left), height(x.right)) + 1;
// 返回新的x节点
return x;
}
- right-right:
Если это дерево справа-направо, ему нужно пройти левый поворот:
Шаги для левого вращения точно противоположны шагам для правого вращения:
- Найти правильный узел узла Z Y Y
- z как левый узел y
- Левый узел y действует как правый узел z
- обновить высоту z
Соответствующий код выглядит следующим образом:
//左旋
Node leftRotate(Node node) {
Node x = node.right;
Node y = x.left;
//左旋操作
x.left = node;
node.right = y;
// 更新node和x的高度
node.height = max(height(node.left), height(node.right)) + 1;
x.height = max(height(x.left), height(x.right)) + 1;
// 返回新的x节点
return x;
}
- лево право:
Если это случай влево вправо, необходимо выполнить левый поворот в левый левый формат, а затем выполните правильную тему, чтобы получить конечный результат.
- право лево:
Если это право-левый формат, вам нужно сначала выполнить правое вращение, преобразовать его в право-правый формат, а затем выполнить левое вращение.
Теперь вопрос, как судить, какого формата дерево? Мы можем судить, получив коэффициент баланса и сравнив вновь вставленные данные:
-
Если баланс> 1, то мы находимся в случае Лево-лево или Лево-право, В это время нам нужно сравнить размер вновь вставленных данных и node.left.data
Если data
Если данные> node.left.data, это означает, что слева направо, вам нужно повернуть node.left влево, а затем повернуть узел вправо
-
Если баланс node.right.data, это означает, что это случай Right Right, и вам нужно только повернуть его влево.
Если данные
Окончательный код для вставки узла выглядит следующим образом:
//插入新节点,从root开始
public void insert(int data){
root=insert(root, data);
}
//遍历插入新节点
Node insert(Node node, int data) {
//先按照普通的BST方法插入节点
if (node == null)
return (new Node(data));
if (data < node.data)
node.left = insert(node.left, data);
else if (data > node.data)
node.right = insert(node.right, data);
else
return node;
//更新节点的高度
node.height = max(height(node.left), height(node.right)) + 1;
//判断节点是否平衡
int balance = getBalance(node);
//节点不平衡有四种情况
//1.如果balance>1,那么我们在Left Left或者left Right的情况,这时候我们需要比较新插入的data和node.left.data的大小
//如果data < node.left.data,表示是left left的情况,只需要一次右旋即可
//如果data > node.left.data,表示是left right的情况,则需要将node.left进行一次左旋,然后将node进行一次右旋
//2.如果balance<-1,那么我们在Right Right或者Right Left的情况,这时候我们需要比较新插入的data和node.right.data的大小
//如果data > node.right.data,表示是Right Right的情况,只需要一次左旋即可
//如果data < node.left.data,表示是Right left的情况,则需要将node.right进行一次右旋,然后将node进行一次左旋
//left left
if (balance > 1 && data < node.left.data)
return rightRotate(node);
// Right Right
if (balance < -1 && data > node.right.data)
return leftRotate(node);
// Left Right
if (balance > 1 && data > node.left.data) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// Right Left
if (balance < -1 && data < node.right.data) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
//返回插入后的节点
return node;
}
Удаление АВЛ
Удаление и вставка AVL аналогичны.
Сначала удалить по обычному BST, а потом еще и ребалансировку делать надо.
Посмотрите интуитивно понятную анимацию:
После удаления возможны 4 случая ребалансировки узлов:
-
Если баланс> 1, то мы находимся в случае с левым левым или левым правым, В это время нам нужно сравнить коэффициент баланса левого узла
Если коэффициент баланса левого узла >=0, это означает, что он левый левый, и требуется только одно правое вращение.
Если коэффициент баланса левого узла
-
Если баланс
Если коэффициент баланса правого узла
Если коэффициент баланса правого узла > 0, что указывает на случай правого левого, то необходимо провести узел.правый декстрозу, и сразу узел L
Соответствующий код выглядит следующим образом:
Node delete(Node node, int data)
{
//Step 1. 普通BST节点删除
// 如果节点为空,直接返回
if (node == null)
return node;
// 如果值小于当前节点,那么继续左节点删除
if (data < node.data)
node.left = delete(node.left, data);
//如果值大于当前节点,那么继续右节点删除
else if (data > node.data)
node.right = delete(node.right, data);
//如果值相同,那么就是要删除的节点
else
{
// 如果是单边节点的情况
if ((node.left == null) || (node.right == null))
{
Node temp = null;
if (temp == node.left)
temp = node.right;
else
temp = node.left;
//没有子节点的情况
if (temp == null)
{
node = null;
}
else // 单边节点的情况
node = temp;
}
else
{ //非单边节点的情况
//拿到右侧节点的最小值
Node temp = minValueNode(node.right);
//将最小值作为当前的节点值
node.data = temp.data;
// 将该值从右侧节点删除
node.right = delete(node.right, temp.data);
}
}
// 如果节点为空,直接返回
if (node == null)
return node;
// step 2: 更新当前节点的高度
node.height = max(height(node.left), height(node.right)) + 1;
// step 3: 获取当前节点的平衡因子
int balance = getBalance(node);
// 如果节点不再平衡,那么有4种情况
//1.如果balance>1,那么我们在Left Left或者left Right的情况,这时候我们需要比较左节点的平衡因子
//如果左节点的平衡因子>=0,表示是left left的情况,只需要一次右旋即可
//如果左节点的平衡因<0,表示是left right的情况,则需要将node.left进行一次左旋,然后将node进行一次右旋
//2.如果balance<-1,那么我们在Right Right或者Right Left的情况,这时候我们需要比较右节点的平衡因子
//如果右节点的平衡因子<=0,表示是Right Right的情况,只需要一次左旋即可
//如果右节点的平衡因子>0,表示是Right left的情况,则需要将node.right进行一次右旋,然后将node进行一次左旋
// Left Left Case
if (balance > 1 && getBalance(node.left) >= 0)
return rightRotate(node);
// Left Right Case
if (balance > 1 && getBalance(node.left) < 0)
{
node.left = leftRotate(node.left);
return rightRotate(node);
}
// Right Right Case
if (balance < -1 && getBalance(node.right) <= 0)
return leftRotate(node);
// Right Left Case
if (balance < -1 && getBalance(node.right) > 0)
{
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node;
}
Кодовый адрес этой статьи:
Эта статья включена вWoohoo. Floyd Press.com/11-AhusbandorIT и…
Самая популярная интерпретация, самая глубокая галантерея, самые краткие уроки и множество трюков, о которых вы не знаете, ждут вас!
Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: «Программируйте эти вещи», разбирайтесь в технологиях, лучше поймите себя!