平衡二叉树
平衡二叉树
当且仅当任何节点的两棵子树的高度差不大于1的自平衡的二叉搜索树
原理
-
平衡因子:某节点的左子树与右子树的高度差
-
失衡节点:在插入/删除的结点向上查找,第一个平衡因子等于2的结点称为失衡节点
-
通过旋转失衡节点,减少高度,将树重新调整为平衡二叉树
理解重点
平衡二叉树的增删和二叉搜索树的增删是一样的
不同在于平衡二叉树增删后可能需要旋转调整
不要边增删边旋转,这样会难以理解(增删后再旋转)
平衡高度代码
balanceHeight()调整失衡节点,失衡节点的平衡因子等于2,故可分为4种情况
增加操作 | 删除操作 | 旋转操作 |
---|---|---|
失衡节点的右节点的右子树增加节点 | 删除左节点且失衡节点的右节点有(左)右子树 | 对失衡节点左旋 |
失衡节点的右节点的左子树增加节点 | 删除左节点且失衡节点的右节点仅有左子树 | 对失衡节点的右节点右旋、对失衡节点左旋 |
失衡节点的左节点的左子树增加节点 | 删除右节点且失衡节点的左节点有左(右)子树 | 对失衡节点右旋 |
失衡节点的左节点的右子树增加节点 | 删除右节点且失衡节点的左节点仅有右子树 | 对失衡节点的左节点左旋、对失衡节点右旋 |
- 失衡节点的平衡因子等于-2说明增加到右节点,再判断是增加到左子树还是右子树
- 失衡节点的平衡因子等于2说明增加到左节点,再判断是增加左子树还是右子树
- 删除节点相当于另一边增加节点(调整代码是一样的)
private void balanceHeight(TreeNode unBalancedNode) {
if (unBalancedNode == null) {
return;
}
if (getBalanceFactor(unBalancedNode) == -2) {
if (getBalanceFactor(unBalancedNode.rightChild) == 1) {
rightRotate(unBalancedNode.rightChild);
}
leftRotate(unBalancedNode);
} else if (getBalanceFactor(unBalancedNode) == 2) {
if (getBalanceFactor(unBalancedNode.leftChild) == -1) {
leftRotate(unBalancedNode.leftChild);
}
rightRotate(unBalancedNode);
}
}
private void leftRotate(TreeNode unBalancedNode) {
TreeNode unBalancedNodeParent = getParent(unBalancedNode);
TreeNode temp = unBalancedNode.rightChild;
if (unBalancedNodeParent == null) {
root = temp;
} else {
unBalancedNodeParent.leftChild = temp;
}
unBalancedNode.rightChild = temp.leftChild;
temp.leftChild = unBalancedNode;
}
private void rightRotate(TreeNode unBalancedNode) {
TreeNode unBalancedNodeParent = getParent(unBalancedNode);
TreeNode temp = unBalancedNode.leftChild;
if (unBalancedNodeParent == null) {
root = temp;
} else {
unBalancedNodeParent.rightChild = temp;
}
unBalancedNode.leftChild = temp.rightChild;
temp.rightChild = unBalancedNode;
}
leftRotate()实现指定节点的左旋,具体为(右旋对称同理):
- 获取其父节点和其右节点
- 若其父节点为空,则其右节点称为新父节点,否则让其父节点指向其右节点(即断开指定节点和其父节点)
- 指定节点的右节点指向其右节点的左节点(即断开指定节点和其右节点,拿到其右节点的左节点)
- 其右节点的左节点指向该节点(即连回指定节点,右节点变成指定节点的父节点)
左旋示意图
右旋示意图
代码实现
- innerMidOrderTraversal():中序遍历验证旋转结果
- innerAdd():从二叉搜索树复制过来的代码,返回增加的节点
- innerSearch()、MinNode()、getSuc()、InnerRemove():从二叉搜索树复制过来的代码,为了找到待删除节点和链接后序节点,返回删除的节点
- getTreeHeight()、getBalanceFactor()、getUnbalancedNode():获取节点高度、平衡因子、失衡节点
- add()、remove():先进行二叉搜索树的增删,返回增删节点,找到对应的失衡节点,调用balanceHeight()
class BalancedBinaryTree {
private class TreeNode {
int value;
TreeNode leftChild;
TreeNode rightChild;
TreeNode(int value) {
this.value = value;
}
}
private TreeNode root;
private void innerMidOrderTraversal(TreeNode root) {
if (root == null) {
return;
}
innerMidOrderTraversal(root.leftChild);
System.out.print(root.value + " ");
innerMidOrderTraversal(root.rightChild);
}
private void leftRotate(TreeNode unBalancedNode) {
TreeNode unBalancedNodeParent = getParent(unBalancedNode);
TreeNode temp = unBalancedNode.rightChild;
if (unBalancedNodeParent == null) { //若失衡节点为根节点,则失衡节点的右节点为新根节点
root = temp;
} else {
unBalancedNodeParent.leftChild = temp; //失衡节点的父节点指向其右节点
}
unBalancedNode.rightChild = temp.leftChild; //失衡节点的右节点指向其右节点的左节点
temp.leftChild = unBalancedNode; //连回失衡节点
}
private void rightRotate(TreeNode unBalancedNode) {
TreeNode unBalancedNodeParent = getParent(unBalancedNode);
TreeNode temp = unBalancedNode.leftChild;
if (unBalancedNodeParent == null) {
root = temp;
} else {
unBalancedNodeParent.rightChild = temp;
}
unBalancedNode.leftChild = temp.rightChild;
temp.rightChild = unBalancedNode;
}
private int getTreeHeight(TreeNode node) {
if (node == null) {
return 0;
}
int leftDepth = getTreeHeight(node.leftChild) + 1;
int rightDepth = getTreeHeight(node.rightChild) + 1;
return Math.max(leftDepth, rightDepth);
}
private int getBalanceFactor(TreeNode node) {
return getTreeHeight(node.leftChild) - getTreeHeight(node.rightChild);
}
private TreeNode getUnbalancedNode(TreeNode node) {
TreeNode parent = getParent(node);
while (parent != null) {
if (Math.abs(getBalanceFactor(parent)) > 1) { // ==2
return parent;
}
parent = getParent(parent);
}
return null;
}
private TreeNode getParent(TreeNode node) {
if (root == node) {
return null;
}
if (node == null) {
return null;
}
TreeNode current = root;
TreeNode parent = null;
while (current != null) {
if (node.value < current.value) {
parent = current;
current = current.leftChild;
} else if (node.value > current.value) {
parent = current;
current = current.rightChild;
} else {
return parent;
}
}
return parent;
}
private void balanceHeight(TreeNode unBalancedNode) {
if (unBalancedNode == null) {
return;
}
System.out.print("旋转前 ——> 中序遍历:");
innerMidOrderTraversal(root);
System.out.println();
if (getBalanceFactor(unBalancedNode) == -2) { //删除左节点 == 右节点添加
if (getBalanceFactor(unBalancedNode.rightChild) == 1) { //右节点的左子树添加
System.out.println("即将右旋");
rightRotate(unBalancedNode.rightChild);
}
System.out.println("即将左旋");
leftRotate(unBalancedNode);
} else if (getBalanceFactor(unBalancedNode) == 2) { //删除右节点 == 左节点添加
if (getBalanceFactor(unBalancedNode.leftChild) == -1) { //左节点的右子树添加
System.out.println("即将左旋");
leftRotate(unBalancedNode.leftChild);
}
System.out.println("即将右旋");
rightRotate(unBalancedNode);
}
System.out.print("旋转后 ——> 中序遍历:");
innerMidOrderTraversal(root);
System.out.println();
}
public void add(int value) {
TreeNode addNode = innerAdd(value);
System.out.print("添加节点为: " + addNode.value);
TreeNode unBalancedNode = getUnbalancedNode(addNode);
System.out.println(" ——> 失衡节点为: " + (unBalancedNode == null ? "null" : unBalancedNode.value));
balanceHeight(unBalancedNode);
}
private TreeNode innerAdd(int value) {
if (root == null) {
root = new TreeNode(value);
return root;
} else {
TreeNode parent;
TreeNode current = root;
while (true) {
parent = current;
if (value < current.value) {
current = current.leftChild;
if (current == null) {
parent.leftChild = new TreeNode(value);
return parent.leftChild;
}
} else {
current = current.rightChild;
if (current == null) {
parent.rightChild = new TreeNode(value);
return parent.rightChild;
}
}
}
}
}
private TreeNode innerSearch(TreeNode root, int value) {
if (root == null) {
return null;
}
if (value < root.value) {
return innerSearch(root.leftChild, value);
} else if (value > root.value) {
return innerSearch(root.rightChild, value);
} else {
return root;
}
}
private TreeNode MinNode(TreeNode node) {
if (node == null) {
return null;
}
TreeNode current = node;
while (current.leftChild != null) {
current = current.leftChild;
}
return current;
}
private TreeNode getSuc(TreeNode node) {
if (node.rightChild != null) {
return MinNode(node.rightChild);
} else {
TreeNode parent = getParent(node);
if (parent == null) {
return null;
}
if (node == parent.leftChild) {
return parent;
} else { //node == parent.rightChild
while (parent != null) {
if (parent.value > node.value) {
break;
}
parent = getParent(node);
}
return parent;
}
}
}
public void remove(int value) {
TreeNode delNode = InnerRemove(value);
System.out.print("删除节点为: " + delNode.value);
TreeNode unBalancedNode = getUnbalancedNode(delNode);
System.out.println(" ——> 失衡节点为: " + (unBalancedNode == null ? "null" : unBalancedNode.value));
balanceHeight(unBalancedNode);
}
private TreeNode InnerRemove(int value) {
TreeNode delNode = innerSearch(root, value);
TreeNode parent = getParent(delNode);
if (delNode.leftChild == null && delNode.rightChild == null) { //删除的节点无左右节点,则断开父节点和它的连接(若是根节点则置空)
if (parent == null) { //delNode = root
root = null;
} else {
if (delNode == parent.leftChild) {
parent.leftChild = null;
} else {
parent.rightChild = null;
}
}
} else if (delNode.leftChild == null && delNode.rightChild != null) { //删除的节点有右节点,则让其右节点连上父节点(若是根节点,则右节点成为新根节点)
if (parent == null) { //delNode = root
root = delNode.rightChild;
} else {
if (delNode == parent.leftChild) {
parent.leftChild = delNode.rightChild;
} else {
parent.rightChild = delNode.rightChild;
}
}
} else if (delNode.leftChild != null && delNode.rightChild == null) { //删除的节点有左节点,则让其左节点连上父节点(若是根节点,则左节点成为新根节点)
if (parent == null) { //delNode = root
root = delNode.leftChild;
} else {
if (delNode == parent.leftChild) {
parent.leftChild = delNode.leftChild;
} else {
parent.rightChild = delNode.leftChild;
}
}
} else { //删除的节点左右节点都有
if (parent == null) { //delNode = root,则左右节点任意一个都可成为新根节点
root = delNode.leftChild;
//root = delNode.rightChild;
} else {
TreeNode successor = getSuc(delNode);
TreeNode successorParent = getParent(successor);
if (successorParent == delNode) { //若后继节点是删除节点的右节点
parent.rightChild = successor;
successor.leftChild = delNode.leftChild;
delNode.leftChild = null;
} else { //若后继节点是删除节点的右子树的左节点
successorParent.leftChild = successor.rightChild; //断开successor
successor.rightChild = null;
parent.leftChild = successor; //del父节点指向successor,即断开del
successor.rightChild = delNode.rightChild; //successor连接delNode右子树
delNode.rightChild = null; //断开delNode右子树
successor.leftChild = delNode.leftChild; //successor连接delNode左子树
delNode.leftChild = null; //断开delNode左子树
}
}
}
return delNode;
}
}
代码测试
这里不讲解平衡二叉树的增删步骤代码,如果不知道请看二叉搜索树
增加测试
失衡节点的右节点的右子树增加节点
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(7);
balancedBinaryTree.add(9);
balancedBinaryTree.add(10);
打印如下
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 10 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:3 5 7 8 9 10
即将左旋
旋转后 ——> 中序遍历:3 5 7 8 9 10
失衡节点的右节点的左子树增加节点
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(7);
balancedBinaryTree.add(9);
balancedBinaryTree.add(6);
打印如下
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:3 5 6 7 8 9
即将右旋
即将左旋
旋转后 ——> 中序遍历:3 5 6 7 8 9
失衡节点的左节点的左子树增加节点
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(8);
balancedBinaryTree.add(5);
balancedBinaryTree.add(9);
balancedBinaryTree.add(3);
balancedBinaryTree.add(7);
balancedBinaryTree.add(2);
打印如下
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 2 ——> 失衡节点为: 8
旋转前 ——> 中序遍历:2 3 5 7 8 9
即将右旋
旋转后 ——> 中序遍历:2 3 5 7 8 9
失衡节点的左节点的右子树增加节点
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(8);
balancedBinaryTree.add(5);
balancedBinaryTree.add(9);
balancedBinaryTree.add(3);
balancedBinaryTree.add(7);
balancedBinaryTree.add(6);
打印如下
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: 8
旋转前 ——> 中序遍历:3 5 6 7 8 9
即将左旋
即将右旋
旋转后 ——> 中序遍历:3 5 6 7 8 9
删除测试
删除左节点且失衡节点的右节点有(左)右子树
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(9);
//balancedBinaryTree.add(7);
balancedBinaryTree.remove(3);
打印如下
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 9 ——> 失衡节点为: null
删除节点为: 3 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:5 8 9
即将左旋
旋转后 ——> 中序遍历:5 8 9
删除左节点且失衡节点的右节点仅有左子树
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(8);
balancedBinaryTree.add(7);
balancedBinaryTree.remove(3);
打印如下
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 8 ——> 失衡节点为: null
添加节点为: 7 ——> 失衡节点为: null
删除节点为: 3 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:5 7 8
即将右旋
即将左旋
旋转后 ——> 中序遍历:5 7 8
删除右节点且失衡节点的左节点有左(右)子树
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(6);
balancedBinaryTree.add(2);
//balancedBinaryTree.add(4);
balancedBinaryTree.remove(6);
打印如下
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: null
添加节点为: 2 ——> 失衡节点为: null
删除节点为: 6 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:2 3 5
即将右旋
旋转后 ——> 中序遍历:2 3 5
删除右节点且失衡节点的左节点仅有右子树
BalancedBinaryTree balancedBinaryTree = new BalancedBinaryTree();
balancedBinaryTree.add(5);
balancedBinaryTree.add(3);
balancedBinaryTree.add(6);
balancedBinaryTree.add(4);
balancedBinaryTree.remove(6);
打印如下
添加节点为: 5 ——> 失衡节点为: null
添加节点为: 3 ——> 失衡节点为: null
添加节点为: 6 ——> 失衡节点为: null
添加节点为: 4 ——> 失衡节点为: null
删除节点为: 6 ——> 失衡节点为: 5
旋转前 ——> 中序遍历:3 4 5
即将左旋
即将右旋
旋转后 ——> 中序遍历:3 4 5