一、认识平衡二叉搜索树
AVL平衡二叉搜索树:
特性
1、拥有BST(二叉搜索树)基本特性
2、左右子树深度之差的绝对值不超过1(左子树节点数减右子树节点数值要等于0、1、-1)
优点
1、拥有BST(二叉搜索树)的优点,并不会使其失衡,从而提高查找运算的速度
缺点
1、为了保持绝对平衡,会进行检查判断,如果失衡则需要旋转去改变树结构,使其保持平衡(频繁插入、删除很影响性能),插入新节点后需要旋转的次数不可预知
认识旋转规则:
平衡二叉树旋转详解
在学习平衡二叉树时,我们需要对平衡二叉树有一些了解,比如:
平衡二叉树旋转分为4种情况 LL型、RR型、LR型、RL型
LL型:
新插入节点在X节点的左子树的左子树中
RR型:
新插入节点在X节点的右子树的右子树中
LR型:
新插入节点在X节点的左子树的右子树中
RL型:
新插入节点在X节点的右子树的左子树中
x节点: 我们进行左旋右旋操作的时候,x节点就是我们旋转的轴
平衡因子: 左子树最大深度 - 右子树最大深度 的绝对值,平衡因子为0、1则为平衡,否则为不平衡
最小不平衡树: 在新插入的结点向上查找,以第一个平衡因子的绝对值超过1的结点为根的子树称为最小不平衡子树
性质:
1、平衡因子不大于0
2、左右子树都为平衡树
LL型图示例:
Java实现
1、定义节点类,使节点与二叉树分离
public class Node {
int value;
Node leftChild;
Node rightChild;
Node(int value) {
this.value = value;
}
public void display() {
System.out.println(this.value + "\t");
}
@Override
public String toString() {
return String.valueOf(value);
}
public int value() {
return value;
}
public void value(int value) {
this.value = value;
}
public Node leftChild() {
return leftChild;
}
public void leftChild(Node leftChild) {
this.leftChild = leftChild;
}
public Node rightChild() {
return rightChild;
}
public void rightChild(Node rightChild) {
this.rightChild = rightChild;
}
}
}
2、逻辑实现类
public class BinaryTree {
private Node root = null;
BinaryTree(int value){
root = new Node(value);
root.rightChild(null);
root.leftChild(null);
}
/**
* 查找
* @param value
* @return
*/
public Node findKey(int value) {
Node tree = root;
int treeValue = tree.value();
while (true) {
if (value == treeValue) {
return tree;
} else if (value < treeValue) {
tree = tree.leftChild();
} else if (value > treeValue) {
tree = tree.rightChild();
}
if (tree == null) {
return null;
}
}
}
/**
* 插入
* @param value
* @return
*/
public boolean insert(int value) {
boolean success = true;
if (null == root) {
new BinaryTree(value);
} else {
Node current = root;
Node parent = null;
while (true) {
if (current.value() > value) {
parent = current;
current = current.leftChild;
if (null == current) {
parent.leftChild(new Node(value));
break;
}
} else if (current.value() < value) {
parent = current;
current = current.rightChild;
if (null == current) {
parent.rightChild(new Node(value));
break;
}
} else {
success = false;
}
}
}
return success;
}
/**
* //中序遍历(递归):
* 1、调用自身来遍历节点的左子树
* 2、访问这个节点
* 3、调用自身来遍历节点的右子树
*/
public void inOrderTraverse() {
System.out.println("中序遍历:");
inOrderTraverse(root);
}
private void inOrderTraverse(Node node) {
if (node == null)
return ;
inOrderTraverse(node.leftChild);
node.display();
inOrderTraverse(node.rightChild);
}
public void inOrderByStack() {
System.out.println("中序非递归遍历");
Stack<Node> stack = new Stack<>();
Node current = root;
while (current != null || stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.leftChild;
}
if (!stack.isEmpty()) {
current = stack.pop();
current.display();
current = current.rightChild;
}
}
}
public void preOrderTraverse() {
System.out.println("前序递归遍历");
preOrderTraverse(root);
}
private void preOrderTraverse(Node node) {
if (null == node) {
return;
}
node.display();
preOrderTraverse(node.leftChild);
preOrderTraverse(node.rightChild);
}
public void preOrderByStack() {
System.out.println("前序遍历非递归操作");
Stack<Node> stack = new Stack<>();
Node current = root;
while (null != current || !stack.isEmpty()) {
while (null != current) {
stack.push(current.leftChild);
current = current.leftChild;
}
if (!stack.isEmpty()) {
current = stack.pop();
current = current.rightChild;
}
}
}
public void postOrderTraverse() {
System.out.println("后序遍历");
postOrderTraverse(root);
}
private void postOrderTraverse(Node node) {
if (null == node) {
return;
}
postOrderTraverse(node.leftChild);
postOrderTraverse(node.rightChild);
node.display();
}
public void postOrderByStack() {
System.out.println("后序遍历非递归操作");
Stack<Node> stack = new Stack<>();
Node current = root;
Node preNode = null;
while (null != current || !stack.isEmpty()) {
while (null != current) {
stack.push(current);
current.display();
current = current.leftChild;
}
if (!stack.isEmpty()) {
current = stack.peek().rightChild;
if (current == null || current == preNode) {
current = stack.pop();
current.display();
preNode = current;
current = null;
}
}
}
}
/**
* 得到最小(大)值
* @return
*/
public int getMinValue() {
Node current = root;
while (true) {
if (null == current.leftChild) {
return current.value();
}
current = current.leftChild;
}
}
public int getMaxValue() {
Node current = root;
while (true) {
if (null == current.rightChild) {
return current.value();
}
current = current.rightChild;
}
}
public boolean delete(int value) {
Node current = root; //需要删除的节点
Node parent = null; //需要删除的节点的父节点
boolean isLeftChild = true; //需要删除的节点是否父节点的左子树
while (true) {
if (value == current.value) {
break;
} else if (value < current.value) {
isLeftChild = true;
parent = current;
current = current.leftChild;
} else {
isLeftChild = false;
parent = current;
current = current.rightChild;
}
//找不到需要删除的节点,直接返回
if (current == null)
return false;
}
//分情况考虑
//1、需要删除的节点为叶子节点
if (current.leftChild == null && current.rightChild == null) {
//如果该叶节点为根节点,将根节点置为null
if (current == root) {
root = null;
} else {
//如果该叶节点是父节点的左子节点,将父节点的左子节点置为null
if (isLeftChild) {
parent.leftChild = null;
} else { //如果该叶节点是父节点的右子节点,将父节点的右子节点置为null
parent.rightChild = null;
}
}
}
//2、需要删除的节点有一个子节点,且该子节点为左子节点
else if (current.rightChild == null) {
//如果该节点为根节点,将根节点的左子节点变为根节点
if (current == root) {
root = current.leftChild;
} else {
//如果该节点是父节点的左子节点,将该节点的左子节点变为父节点的左子节点
if (isLeftChild) {
parent.leftChild = current.leftChild;
} else { //如果该节点是父节点的右子节点,将该节点的左子节点变为父节点的右子节点
parent.rightChild = current.leftChild;
}
}
}
//2、需要删除的节点有一个子节点,且该子节点为右子节点
else if (current.leftChild == null) {
//如果该节点为根节点,将根节点的右子节点变为根节点
if (current == root) {
root = current.rightChild;
} else {
//如果该节点是父节点的左子节点,将该节点的右子节点变为父节点的左子节点
if (isLeftChild) {
parent.leftChild = current.rightChild;
} else { //如果该节点是父节点的右子节点,将该节点的右子节点变为父节点的右子节点
parent.rightChild = current.rightChild;
}
}
}
//3、需要删除的节点有两个子节点,需要寻找该节点的后续节点替代删除节点
else {
Node successor = getSuccessor(current);
//如果该节点为根节点,将后继节点变为根节点,并将根节点的左子节点变为后继节点的左子节点
if (current == root) {
root = successor;
} else {
//如果该节点是父节点的左子节点,将该节点的后继节点变为父节点的左子节点
if (isLeftChild) {
parent.leftChild = successor;
} else { //如果该节点是父节点的右子节点,将该节点的后继节点变为父节点的右子节点
parent.rightChild = successor;
}
}
}
current = null;
return true;
} //删除
/**
*
* 得到后继节点,即删除节点的左后代
*/
private Node getSuccessor(Node delNode) {
Node successor = delNode;
Node successorParent = null;
Node current = delNode.rightChild;
while (current != null) {
successorParent = successor;
successor = current;
current = current.leftChild;
}
//如果后继节点不是删除节点的右子节点时,
if (successor != delNode.rightChild) {
//要将后继节点的右子节点指向后继结点父节点的左子节点,
successorParent.leftChild = successor.rightChild;
//并将删除节点的右子节点指向后继结点的右子节点
successor.rightChild = delNode.rightChild;
}
//任何情况下,都需要将删除节点的左子节点指向后继节点的左子节点
successor.leftChild = delNode.leftChild;
return successor;
}
3、测试验证
public static void main(String[] args) {
BinaryTree bt = new BinaryTree(52);
bt.insert(580);
bt.insert(12);
bt.insert(50);
bt.insert(58);
bt.insert(9);
bt.insert(888);
bt.insert(248);
bt.insert(32);
bt.insert(666);
bt.insert(455);
bt.insert(777);
bt.insert(999);
bt.inOrderTraverse();
bt.preOrderTraverse();
bt.postOrderTraverse();
System.out.println(bt.findKey(32));
System.out.println(bt.findKey(81));
System.out.println("最小值:" + bt.getMinValue());
bt.delete(52); //删除有两个子节点的节点,且删除节点为根节点
bt.inOrderTraverse();
}