目录
当一棵树越来越大时,这个时候要把大树想成小树,对应上面的旋转就可以了,这里只说明一种情况了
二叉树是什么?
二叉树是一种树的结构,我们通过将数据以树的结构进行存储,方便的是我们对数据进行检索。
但是一棵不平衡的二叉树,如果进行数据检索,性能来说会丢失,如果要达到最优检索效果,需要一棵平衡的二叉树。
平衡是什么?
平衡就是树的每一个结点,相对的左右子树深度差值不能大于1或小于-1
新增不平衡有哪些情况?
最终不平衡原因有以下四种情况
最终不平衡
最终不平衡原因有以下四种情况原因有以下四种情况
针对不平衡该如何旋转?
情况1》右旋
条件:插入到不平衡节点的左子树的左子树上
情况2》先左旋再右旋
条件:插入到不平衡节点左子树的右子树上
情况3》先右旋再左旋
条件:插入到不平衡节点右子树的左子树上
情况4》左旋
条件:插入到不平衡节点右子树的右子树上
平衡演示
当一棵树越来越大时,这个时候要把大树想成小树,对应上面的旋转就可以了,这里只说明一种情况了
最终旋转的条件为
旋转方向 | 说明 |
右旋 | 不平衡节点左子树最深,左子树的左子树最深 |
先左旋再右旋 | 不平衡节点左子树最深,左子树的右子树最深 |
先右旋再左旋 | 不平衡节点右子树最深,右子树的左子树最深 |
左旋 | 不平衡节点右子树最深,右子树的右子树最深 |
树结点删除
位置 | 情况 |
图中1 | 没有左子树,也没有右子树 |
图中2 | 只有一个左子树 |
图中3 | 只有一个右子树 |
图中4 | 有左子树,也有右子树 |
操作说明
图位置 | |
图中1 | 1.直接删除20节点 2.15结点右子树指向设置为null 3. 从20的父结点开始向上走,发现不平衡结点就旋转,直到走到根结点为止 |
图中2 | 1.让6结点指向父结点10的引用,改成10指向父结点15的引用 2.15结点左结点的引用,改成指向6结点的引用 3.删除结点10,然后从10的父结点开始向上走,发现不平衡结点就旋转,直到走到根结点为止 |
图中3 | 1.让99结点指向父结点90的引用,改成90指向父结点80的引用 2.80结点右结点的引用,改成指向99结点的引用 3.删除结点90,然后从90的父结点开始向上走,发现不平衡结点就旋转,直到走到根结点为止 |
图中4 | 1.首先找50结点右子树,最左边的结点70 2. 把70结点的值保存一下,然后删除70结点 3. 从70的父结点开始向上走,发现不平衡结点就旋转,直到走到根结点为止 4.将70结点的值把50结点值覆盖掉 |
代码实现
树结构代码
package com.wxl.practices.tree;
public class Tree<K extends Comparable, V> {
private TreeNode<K, V> root; //根结点
private int size;
/**
* 当前树上元素个数
* @return
*/
public int size() {
return size;
}
/**
* 树结点的结构
* @param <K>
* @param <V>
*/
class TreeNode<K, V>{
private TreeNode<K, V> parent;
private K key;
private V value;
private TreeNode<K, V> left;
private TreeNode<K, V> right;
public TreeNode(K key, V value) {
this.key = key;
this.value = value;
}
public TreeNode(TreeNode<K, V> parent, K key, V value) {
this(key, value);
this.parent = parent;
}
}
/**
* find方法为返回多个值创建的返回值类
*/
private class FindResult {
public boolean isFind;
public TreeNode<K, V> node;
public boolean isLeft;
public FindResult(boolean isFind, TreeNode<K, V> node) {
this.isFind = isFind;
this.node = node;
}
public FindResult(boolean isFind, TreeNode<K, V> node, boolean isLeft) {
this.isFind = isFind;
this.node = node;
this.isLeft = isLeft;
}
}
/**
* 寻找树结点,存在或不存在,以下新增或删除会用到
* @param key
* @return
*/
private FindResult find(K key) {
TreeNode<K, V> node = root;
int rst = 0;
do {
rst = key.compareTo(node.key);
if (rst == 0) {
return new FindResult(true, node);
} else if (rst > 0) {
if (node.right != null) {
node = node.right;
continue;
}
} else if (rst < 0) {
if (node.left != null) {
node = node.left;
continue;
}
}
break;
} while (true);
return new FindResult(false, node, rst < 0 ? true : false);
}
public void add(K key, V value) {
if(root == null) {
root = new TreeNode(key, value);
} else {
FindResult findResult = find(key);
if (findResult.isFind) {
findResult.node.value = value;
} else {
TreeNode<K, V> newNode = new TreeNode(findResult.node, key, value);
if (findResult.isLeft) {
findResult.node.left = newNode;
} else {
findResult.node.right = newNode;
}
//每一次新加完结点的时候都要判断有没有因为新增结点,导至树不平衡的情况
balanceTree(findResult.node);
}
}
size ++;
}
/**
* 调用平衡树方法
*/
private void balanceTree(TreeNode<K, V> node) {
int leftRightTreeDValue = 0;
TreeNode<K, V> leftNode;
TreeNode<K, V> rightNode;
while (node != null) {
leftRightTreeDValue = leftRightTreeDValue(node);
if (leftRightTreeDValue > 1) { //左边深并且不平衡
leftNode = node.left;
if(leftRightTreeDValue(leftNode) < 0) {
leftRotation(leftNode);
}
rightRotation(node);
} else if (leftRightTreeDValue < -1) { //右边深并且不平衡
rightNode = node.right;
if(leftRightTreeDValue(rightNode) > 0) {
rightRotation(rightNode);
}
leftRotation(node);
}
node = node.parent;
}
}
/**
* 计算要查询结点左右两个树的差值
* @param node 树结点
* @return
*/
private int leftRightTreeDValue(TreeNode<K, V> node) {
return countTreeDepth(node.left) - countTreeDepth(node.right);
}
/**
* 计算一个树的深度
* @param node
* @return
*/
private int countTreeDepth(TreeNode<K, V> node) {
if (node == null) {
return 0;
}
return 1 + Math.max(countTreeDepth(node.left), countTreeDepth(node.right));
}
/**
* 左旋转
* @param node
*/
private void leftRotation(TreeNode<K, V> node) {
TreeNode<K, V> rightNode = node.right;
node.right = rightNode.left;
if (null != rightNode.left) {
rightNode.left.parent = node;
}
rightNode.parent = node.parent;
node.parent = rightNode;
rightNode.left = node;
if (null == rightNode.parent) {
this.root = rightNode;
} else if (rightNode.parent.left == node) {
rightNode.parent.left = rightNode;
} else if (rightNode.parent.right == node) {
rightNode.parent.right = rightNode;
}
}
/**
* 右旋转
* @param node
*/
private void rightRotation(TreeNode<K, V> node) {
TreeNode<K, V> leftNode = node.left;
node.left = leftNode.right;
if (null != leftNode.right) {
leftNode.right.parent = node;
}
leftNode.parent = node.parent;
node.parent = leftNode;
leftNode.right = node;
if (null == leftNode.parent) {
this.root = leftNode;
} else if (leftNode.parent.left == node) {
leftNode.parent.left = leftNode;
} else if (leftNode.parent.right == node) {
leftNode.parent.right = leftNode;
}
}
/**
* 删除结点
* @param key
*/
public void remove(K key) {
FindResult findResult = find(key);
if (!findResult.isFind) {
return;
}
TreeNode<K, V> parentNode = findResult.node.parent;
TreeNode<K, V> leftNode = findResult.node.left;
TreeNode<K, V> rightNode = findResult.node.right;
if (leftNode == null && rightNode == null) {
relationNode(parentNode, findResult.node, null);
} else if(leftNode != null && rightNode == null) {
relationNode(parentNode, findResult.node, leftNode);
} else if(leftNode == null && rightNode != null) {
relationNode(parentNode, findResult.node, rightNode);
} else {
TreeNode<K, V> right = findRight(findResult.node);
K rightKey = right.key;
V rightValue = right.value;
remove(right.key);
findResult.node.key = rightKey;
findResult.node.value = rightValue;
return;
}
releaseNode(findResult.node);
findResult.node = null;
balanceTree(parentNode);
size --;
}
/**
*
* @param parentNode 父结点
* @param node 要删除的结点
* @param resultNode 根据上面代码可见结点意思
*/
private void relationNode(TreeNode<K, V> parentNode, TreeNode<K, V> node,
TreeNode<K, V> resultNode) {
if(resultNode != null)
resultNode.parent = node.parent;
if(parentNode == null) {
this.root = resultNode;
} else if(parentNode.left == node) {
parentNode.left = resultNode;
} else if(parentNode.right == node) {
parentNode.right = resultNode;
}
}
/**
* 寻找一个结点右子树的最左结点
* @param node 要删除的结点
* @return
*/
private TreeNode<K, V> findRight(TreeNode<K, V> node) {
TreeNode<K, V> rightNode = node.right;
while (rightNode.left != null) {
rightNode = rightNode.left;
}
return rightNode;
}
/**
* 释放结点
* @param node
*/
private void releaseNode(TreeNode<K, V> node) {
node.parent = null;
node.key = null;
node.value = null;
node.left = null;
node.right = null;
}
}
测试代码
package com.wxl.practices.tree;
public class Test {
public static void main(String[] args) {
Tree<String, Integer> tr = new Tree<>();
//新增
tr.add("70", 1001);
tr.add("50", 1002);
tr.add("20", 1003);
tr.add("10", 1004);
tr.add("15", 1005);
tr.add("80", 1006);
tr.add("75", 1007);
tr.add("90", 1008);
tr.add("99", 1009);
tr.add("76", 1010);
tr.add("33", 1011);
tr.add("39", 1012);
System.out.println(tr.size());
//删除
tr.remove("50");
tr.remove("90");
tr.remove("39");
tr.remove("75");
tr.remove("80");
tr.remove("99");
System.out.println(tr.size());
}
}