AVL Tree
Data Structure · Stretch
Step through it: Binary Tree Traversal pattern
Step 1 of 5
A tree has no single first-to-last order, so we pick one. The three common orders differ only in when a node is visited compared with its children.
Step 2 of 5
Pre-order: the node first, then left, then right. Good for copying a tree.
Step 3 of 5
In-order: left, then the node, then right. On a binary search tree this gives the values in sorted order.
Step 4 of 5
Post-order: left, right, then the node. Children come before their parent, so it is the order for deleting a tree or adding up sizes.
Step 5 of 5
Your turn.
In computer science, an AVL tree (named after inventors
Adelson-Velsky and Landis) is a self-balancing binary search
tree. It was the first such data structure to be invented.
In an AVL tree, the heights of the two child subtrees of any
node differ by at most one; if at any time they differ by
more than one, rebalancing is done to restore this property.
Lookup, insertion, and deletion all take O(log n) time in
both the average and worst cases, where n is the number of
nodes in the tree prior to the operation. Insertions and
deletions may require the tree to be rebalanced by one or
more tree rotations.
Animation showing the insertion of several elements into an AVL tree. It includes left, right, left-right and right-left rotations.
AVL tree with balance factors (green)
AVL Tree Rotations
Left-Left Rotation
Right-Right Rotation
Left-Right Rotation
Right-Left Rotation
Code
import BinarySearchTree from '../binary-search-tree/BinarySearchTree';
export default class AvlTree extends BinarySearchTree {
/**
* @param {*} value
*/
insert(value) {
// Do the normal BST insert.
super.insert(value);
// Let's move up to the root and check balance factors along the way.
let currentNode = this.root.find(value);
while (currentNode) {
this.balance(currentNode);
currentNode = currentNode.parent;
}
}
/**
* @param {*} value
* @return {boolean}
*/
remove(value) {
// Do standard BST removal.
super.remove(value);
// Balance the tree starting from the root node.
this.balance(this.root);
}
/**
* @param {BinarySearchTreeNode} node
*/
balance(node) {
// If balance factor is not OK then try to balance the node.
if (node.balanceFactor > 1) {
// Left rotation.
if (node.left.balanceFactor > 0) {
// Left-Left rotation
this.rotateLeftLeft(node);
} else if (node.left.balanceFactor < 0) {
// Left-Right rotation.
this.rotateLeftRight(node);
}
} else if (node.balanceFactor < -1) {
// Right rotation.
if (node.right.balanceFactor < 0) {
// Right-Right rotation
this.rotateRightRight(node);
} else if (node.right.balanceFactor > 0) {
// Right-Left rotation.
this.rotateRightLeft(node);
}
}
}
/**
* @param {BinarySearchTreeNode} rootNode
*/
rotateLeftLeft(rootNode) {
// Detach left node from root node.
const leftNode = rootNode.left;
rootNode.setLeft(null);
// Make left node to be a child of rootNode's parent.
if (rootNode.parent) {
rootNode.parent.setLeft(leftNode);
} else if (rootNode === this.root) {
// If root node is root then make left node to be a new root.
this.root = leftNode;
}
// If left node has a right child then detach it and
// attach it as a left child for rootNode.
if (leftNode.right) {
rootNode.setLeft(leftNode.right);
}
// Attach rootNode to the right of leftNode.
leftNode.setRight(rootNode);
}
/**
* @param {BinarySearchTreeNode} rootNode
*/
rotateLeftRight(rootNode) {
// Detach left node from rootNode since it is going to be replaced.
const leftNode = rootNode.left;
rootNode.setLeft(null);
// Detach right node from leftNode.
const leftRightNode = leftNode.right;
leftNode.setRight(null);
// Preserve leftRightNode's left subtree.
if (leftRightNode.left) {
leftNode.setRight(leftRightNode.left);
leftRightNode.setLeft(null);
}
// Attach leftRightNode to the rootNode.
rootNode.setLeft(leftRightNode);
// Attach leftNode as left node for leftRight node.
leftRightNode.setLeft(leftNode);
// Do left-left rotation.
this.rotateLeftLeft(rootNode);
}
/**
* @param {BinarySearchTreeNode} rootNode
*/
rotateRightLeft(rootNode) {
// Detach right node from rootNode since it is going to be replaced.
const rightNode = rootNode.right;
rootNode.setRight(null);
// Detach left node from rightNode.
const rightLeftNode = rightNode.left;
rightNode.setLeft(null);
if (rightLeftNode.right) {
rightNode.setLeft(rightLeftNode.right);
rightLeftNode.setRight(null);
}
// Attach rightLeftNode to the rootNode.
rootNode.setRight(rightLeftNode);
// Attach rightNode as right node for rightLeft node.
rightLeftNode.setRight(rightNode);
// Do right-right rotation.
this.rotateRightRight(rootNode);
}
/**
* @param {BinarySearchTreeNode} rootNode
*/
rotateRightRight(rootNode) {
// Detach right node from root node.
const rightNode = rootNode.right;
rootNode.setRight(null);
// Make right node to be a child of rootNode's parent.
if (rootNode.parent) {
rootNode.parent.setRight(rightNode);
} else if (rootNode === this.root) {
// If root node is root then make right node to be a new root.
this.root = rightNode;
}
// If right node has a left child then detach it and
// attach it as a right child for rootNode.
if (rightNode.left) {
rootNode.setRight(rightNode.left);
}
// Attach rootNode to the left of rightNode.
rightNode.setLeft(rootNode);
}
}References
Practice
Binary Tree Traversal
- Binary Tree Inorder TraversalEasy
- Binary Tree Preorder TraversalEasy
- Binary Tree Postorder TraversalEasy
- Same TreeEasy
- Symmetric TreeEasy
- Invert Binary TreeEasy
- Subtree of Another TreeEasy
- Merge Two Binary TreesEasy
- Construct Binary Tree from Preorder and Inorder TraversalMedium
- Construct Binary Tree from Inorder and Postorder TraversalMedium
- Validate Binary Search TreeMedium
- Kth Smallest Element in a BSTMedium
- Flatten Binary Tree to Linked ListMedium
- Lowest Common Ancestor of a Binary TreeMedium
- Binary Search Tree IteratorMedium
- Recover Binary Search TreeMedium
- Serialize and Deserialize Binary TreeHard
- Binary Tree Maximum Path SumHard
Spotted a mistake or something unclear? Report it on GitHub.