平衡二叉树|B树|B+树
1. 平衡二叉树
- 平衡二叉树解决了二叉排序树退化为单链表的问题;
- 平衡二叉树也称为自平衡二叉搜索树或AVL树;
- AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis;
- 平衡二叉树本质还是二叉排序树;
- 平衡二叉树的限制:空树或左右子树高度差的绝对值不超过1;
- 平衡因子:左子树高度-右子树高度,取值范围为{-1,0,1};
- 调整最小不平衡子树使得整个二叉排序树平衡;
- 查找时间复杂度:O(log2n),借助递推公式:Nh=Nh-1+Nh-2+1;
1.1 如何调整最小不平衡子树
- 四种不平衡子树:LL、LR、RL、RR,分别表示1)在root树的左子节点的左子树插入节点导致不平衡;1)在root树的左子节点的右子树插入节点导致不平衡;1)在root树的右子节点的左子树插入节点导致不平衡;1)在root树的右子节点的右子树插入节点导致不平衡;
- LL、RR为两种基本情况,对应解决方案为单旋转,分别为:右旋、左旋;
LL:树右旋
RR:树左旋
- LR、RL为两种复杂情况,对应解决方案为双旋转,分别为:先左旋再右旋、先右旋再左旋;
LR:左子树左旋+树右旋
RL:右子树右旋+树左旋
1.2 左旋、右旋代码实现
左旋:
/**
* 左旋
*/
private void leftRotate() {
// 1.创建新节点,与当前根节点值一致
TreeNode newNode = new TreeNode(val);
// 2.新节点的左子树为root的左子树
newNode.left=left;
// 3. 新节点的右子树为root的右子树的左子树
newNode.right=right.left;
// 4.root的值替换为其右子节点的值
val=right.val;
// 5.root的右子树为右子树的右子树
right=right.right;
// 6.当前节点的左子树为新节点
left=newNode;
}
右旋:
/**
* 右旋
*/
private void rightRotate() {
// 1.创建新节点,与当前根节点值一致
TreeNode newNode = new TreeNode(val);
// 2.新节点的右子树为root的右子树
newNode.right=right;
// 3. 新节点的左子树为root的左子树的右子树
newNode.left=left.right;
// 4.root的值替换为其左子节点的值
val=left.val;
// 5.root的左子树为左子树的左子树
left=left.left;
// 6.当前节点的右子树为新节点
right=newNode;
}
1.3 解决导致不平衡的四种情况
class AVLTree{
public TreeNode root;
public AVLTree(TreeNode root){
this.root=root;
}
public AVLTree() {
}
/**
* 插入节点
*/
public void insert(TreeNode node){
// 根节点
if (root==null){
root=node;
}else{
root.insert(node);
}
}
/**
* 中序遍历
*/
public void inOrder(){
if (root==null){
return;
}
root.inOrder();
}
/**
* 层序遍历
*/
public void levelOrder(){
if (root==null){
return;
}
root.levelOrder();
}
}
class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val){
this.val=val;
}
public TreeNode(int val,TreeNode left,TreeNode right){
this.val=val;
this.left=left;
this.right=right;
}
/**
* 返回以当前节点为根节点的树的高度
* @return
*/
public int getHeight(){
return Math.max(left==null?0:left.getHeight(),right==null?0:right.getHeight())+1;
}
/**
* 返回以当前节点左子树的高度
* @return
*/
public int getLeftHeight(){
if (left==null){
return 0;
}
return left.getHeight();
}
/**
* 返回以当前节点右子树的高度
* @return
*/
public int getRightHeight(){
if (right==null){
return 0;
}
return right.getHeight();
}
@Override
public String toString() {
return "TreeNode{" +
"val=" + val +
'}';
}
/**
* 新增节点,满足二叉排序树要求
* @param node
*/
public void insert(TreeNode node) {
if (node==null){
return;
}
// 比较当前节点与node节点的大小关系
if (node.val<val){
if (left==null){
left=node;
}else{
left.insert(node);
}
}else{
if (right==null){
right=node;
}else{
right.insert(node);
}
}
// 左旋:右子树高度过大
if (getRightHeight()-getLeftHeight()>1){
// RR:右子树的右子树上插入节点导致不平衡
if (right.getRightHeight()-right.getLeftHeight()>=1){
leftRotate();
}else if (right.getLeftHeight()-right.getRightHeight()>=1){ // RL:右子树的左子树上插入节点导致不平衡
// 右子树进行右旋
right.rightRotate();
// 整棵树左旋
leftRotate();
}
}else if (getLeftHeight()-getRightHeight()>1){ // 右旋:左子树高度过大
// LL:左子树的左子树上插入节点导致不平衡
if (left.getLeftHeight()-left.getRightHeight()>=1){
rightRotate();
}else if (left.getRightHeight()-left.getLeftHeight()>=1){ // LR:右左子树的右子树上插入节点导致不平衡
// 左子树进行左旋
left.leftRotate();
// 整棵树右旋
rightRotate();
}
}
}
/**
* 左旋
*/
private void leftRotate() {
// 1.创建新节点,与当前根节点值一致
TreeNode newNode = new TreeNode(val);
// 2.新节点的左子树为root的左子树
newNode.left=left;
// 3. 新节点的右子树为root的右子树的左子树
newNode.right=right.left;
// 4.root的值替换为其右子节点的值
val=right.val;
// 5.root的右子树为右子树的右子树
right=right.right;
// 6.当前节点的左子树为新节点
left=newNode;
}
/**
* 右旋
*/
private void rightRotate() {
// 1.创建新节点,与当前根节点值一致
TreeNode newNode = new TreeNode(val);
// 2.新节点的右子树为root的右子树
newNode.right=right;
// 3. 新节点的左子树为root的左子树的右子树
newNode.left=left.right;
// 4.root的值替换为其左子节点的值
val=left.val;
// 5.root的左子树为左子树的左子树
left=left.left;
// 6.当前节点的右子树为新节点
right=newNode;
}
/**
* 中序遍历
*/
public void inOrder(){
if (left!=null){
left.inOrder();
}
System.out.println("node:"+val);
if (right!=null){
right.inOrder();
}
}
/**
* 层序遍历
*/
public void levelOrder() {
Deque<TreeNode> queue=new ArrayDeque<>();
queue.offer(this);
while (!queue.isEmpty()){
TreeNode cur = queue.poll();
System.out.println("node:"+cur.val);
if (cur.left!=null){
queue.offer(cur.left);
}
if (cur.right!=null){
queue.offer(cur.right);
}
}
}
}
2. B树|B+树
- 二叉树实现简单,但由于其可能拥有极深的高度,往往实用价值不高;
- n叉树的提出解决了二叉树的高度问题,可以降低二叉树高度;
- n叉树:一个节点可以拥有两个以上的子节点的树称为n叉树;
- 典型的n叉树:红黑树、2-3树、2-3-4树、B树;
2.1 2-3树
- 本质仍为排序树,需满足左<根<右的特点;
- 2-3树由2节点、3节点构成;
- 三叉B树;
2.2 B树的插入和删除
插入:
- 通过B树关键字大小关系找到插入最底端位置,并进行放置;
- 如果此处节点关键字数量超过最大关键字数量限制,则将本节点从中间位置一分为二,中间位置的元素插入父节点;
删除:
资料来源:1)尚硅谷;2)王道考研