手撕AVL树
在开始正式学习之前,引用一句很经典的话,介绍给大家,也给自己留一个mark:
数据结构 - 定义一种性值并且维护这种性值
1. 二叉排序树
性值:
- 左子树 < 根节点
- 右子树 > 根节点
用途:解决与排序相关的搜索问题
操作: - 插入:
- 删除:
* 删除度为0的节点:直接删除即可
* 删除度为1的节点:删除后,将唯一子树连接到父节点
* 删除度为2的节点:找到前驱节点(小于当前节点的最大节点),将当前删除节点值替换为前驱节点值,并在左子树中删除前驱节点(该前驱节点一定是度为0或1的节点);
代码实现:
public class Binary_search_tree {
public TreeNode getNewNode(int key) {
return new TreeNode(key);
}
public TreeNode insert(TreeNode root, int key) {
if (root == null) {
return getNewNode(key);
}
if (key == root.val) {
return root;
}
if (key < root.val) {
root.left = insert(root.left, key);
return root;
}
root.right = insert(root.right, key);
return root;
}
public TreeNode erase(TreeNode root, int key) {
if (root == null) return null;
if (key < root.val) {
root.left = erase(root.left, key);
} else if (key > root.val) {
root.right = erase(root.right, key);
} else {
if (root.left == null || root.right == null) {
TreeNode temp = root.left == null ? root.right : root.left;
root = null;
return temp;
} else {
TreeNode temp = preDeccessor(root);
root.val = temp.val;
root.left = erase(root.left, temp.val);
}
}
return root;
}
private TreeNode preDeccessor(TreeNode root) {
TreeNode temp = root.left;
while (temp.right != null) {
temp = temp.right;
}
return temp;
}
public void clear(TreeNode root) {
if (root == null) return;
clear(root.left);
clear(root.right);
root = null;
}
public static void main(String[] args) {
Binary_search_tree tree = new Binary_search_tree();
TreeNode root = null;
Scanner scanner = new Scanner(System.in);
System.out.println("Enter two number: ");
while (true) {
String line = scanner.nextLine();
String[] strings = line.split(", ");
int op = Integer.parseInt(strings[0]);
int val = Integer.parseInt(strings[1]);
switch (op) {
case 0: {
root = tree.insert(root, val);
break;
}
case 1:
root = tree.erase(root, val);
}
outPut(root);
System.out.println();
}
}
private static void outPut(TreeNode root) {
if (root == null) return;
outPut(root.left);
System.out.print(root.val + ",");
outPut(root.right);
}
}
2. AVL树
性值:| H(left) - H(right) | <= 1
优点:对每个节点左右子树的高度做了限制,所以整颗树不会退化成链表
下面才是经典~ 这一对互逆的操作非常优美的维护了AVL树的性值~
左旋
右旋
重点来了~
失衡类型
LL型: 右旋
RL型:右旋+左旋
LR型:左旋+右旋
RR型:左旋
手撕代码:
public class AVLTree {
static Node NIL = new Node(0, 0, null, null);
static class Node {
int key;
int h;
Node left;
Node right;
public Node(int key, int h, Node left, Node right) {
this.key = key;
this.h = h;
this.left = left;
this.right = right;
}
public Node(int key, int h) {
this.key = key;
this.h = h;
this.left = NIL;
this.right = NIL;
}
}
private void updateHeight(Node root) {
root.h = Math.max(root.left.h, root.right.h) + 1;
}
final String[] TYPE = {"", "LL", "RR", "LR", "RL"};
private Node maintain(Node root) {
if (Math.abs(root.left.h - root.right.h) < 2) return root;
int type = -1;
System.out.println("maintain: " + root.key);
if (root.left.h > root.right.h) {
if (root.left.right.h > root.left.left.h) {
System.out.println(root.left.key + " left rotate");
root.left = leftRotate(root.left);
type = 3;
}
System.out.println(root.key + " right rotate");
root = rightRotate(root);
if (type == -1) {
type = 1;
}
} else {
if (root.right.left.h > root.right.right.h) {
System.out.println(root.right.key + " right rotate");
root.right = rightRotate(root.right);
type = 4;
}
System.out.println(root.key + " left rotate");
root = leftRotate(root);
if (type == -1) {
type = 2;
}
}
System.out.println("maintain type = " + TYPE[type]);
return root;
}
private Node leftRotate(Node root) {
Node newRoot = root.right;
root.right = newRoot.left;
newRoot.left = root;
updateHeight(root);
updateHeight(newRoot);
return newRoot;
}
private Node rightRotate(Node root) {
Node newRoot = root.left;
root.left = newRoot.right;
newRoot.right = root;
updateHeight(root);
updateHeight(newRoot);
return newRoot;
}
public Node erase(Node root, int key) {
if (root == NIL) return root;
if (key < root.key) {
root.left = erase(root.left, key);
} else if (key > root.key) {
root.right = erase(root.right, key);
} else {
if (root.left == NIL || root.right == NIL) {
Node temp = root.left == null ? root.right : root.left;
root = null;
return temp;
} else {
Node temp = precursor(root);
root.key = temp.key;
root.left = erase(root.left, temp.key);
}
}
updateHeight(root);
return maintain(root);
}
private Node precursor(Node root) {
Node temp = root.left;
while (temp.right != NIL) {
temp = temp.right;
}
return temp;
}
public static void outPut(Node root) {
if (root == NIL) return;
System.out.print(root.key + ", ");
outPut(root.left);
outPut(root.right);
}
public Node getNewNode(int key) {
return new Node(key, 1);
}
public Node insert(Node root, int key) {
if (root == NIL) {
return getNewNode(key);
}
if (key == root.key) {
return root;
}
if (key < root.key) {
root.left = insert(root.left, key);
} else {
root.right = insert(root.right, key);
}
updateHeight(root);
return maintain(root);
}
public static void main(String[] args) {
AVLTree tree = new AVLTree();
Node root = NIL;
Scanner scanner = new Scanner(System.in);
System.out.println("Enter two number: ");
while (true) {
String line = scanner.nextLine();
String[] strings = line.split(", ");
int op = Integer.parseInt(strings[0]);
int val = Integer.parseInt(strings[1]);
switch (op) {
case 0: {
root = tree.insert(root, val);
break;
}
case 1:
root = tree.erase(root, val);
}
outPut(root);
System.out.println();
}
}
}
运行结果:
Enter two number:
0, 5
--------AVL tree print-------
5,
--------AVL tree print done -------
0, 9
--------AVL tree print-------
5, 9,
--------AVL tree print done -------
0, 8
--------AVL tree print-------
maintain: 5
9 right rotate
5 left rotate
maintain type = RL
8, 5, 9,
--------AVL tree print done -------
0, 3
--------AVL tree print-------
8, 5, 3, 9,
--------AVL tree print done -------
0, 2
--------AVL tree print-------
maintain: 5
5 right rotate
maintain type = LL
8, 3, 2, 5, 9,
--------AVL tree print done -------
0, 4
--------AVL tree print-------
maintain: 8
3 left rotate
8 right rotate
maintain type = LR
5, 3, 2, 4, 8, 9,
--------AVL tree print done -------
0, 1
--------AVL tree print-------
5, 3, 2, 1, 4, 8, 9,
--------AVL tree print done -------
0, 7
--------AVL tree print-------
5, 3, 2, 1, 4, 8, 7, 9,
--------AVL tree print done -------
这篇文章是基础,也是重中之重。掌握了精髓,解决相关的算法题目会觉得轻松很多~