5. 二叉树

1 树的基本概念
  1. 节点、根节点、父节点、子节点、兄弟节点
  2. 空树:一棵树可以没有任何节点
  3. 一棵树可以只有 1 个节点,这个节点其实就是根节点
  4. 子树、左子树、右子树
  5. 节点的度:子树的个数
  6. 树的度:所有节点度中的最大值
  7. 叶子节点:度为 0 的节点
  8. 非叶子节点:度不为 0 的节点
  9. 层数(level):根节点在第 1 层,根节点的子节点在第 2 层,以此类推(有些教程也从第 0 层开始计算)
  10. 节点的深度(depth):从根节点到当前节点的唯一路径上的节点总数,即从上往下数
  11. 节点的高度(height):从当前节点到最远叶子节点的路径上的节点总数,即从下往上数
  12. 树的深度:所有节点深度中的最大值
  13. 树的高度:所有节点高度中的最大值
  14. 树的深度=树的高度
  15. 有序树:树中任意节点的子节点之间有顺序关系
  16. 无序树、自由树:树中任意节点的子节点之间没有顺序关系
  17. 森林:由m(m ≥ 0)棵互不相交的树组成的集合
2 二叉树(Binary Tree)
  1. 每个节点的度最大为2(最多拥有 2 棵子树)
  2. 左子树和右子树是有顺序的
  3. 即使某节点只有一棵子树,也要区分左右子树
2.1 二叉树性质
  1. 非空二叉树的第 i 层,最多有2^(i − 1)个节点( i ≥ 1 )
  2. 在高度为 h 的二叉树上最多有 2^h − 1 个结点(h ≥ 1 )
  3. 对于任何一棵非空二叉树,如果叶子节点个数为 n0,度为 2 的节点个数为 n2,则有: n0 = n2 + 1
    1. 二叉树节点总数n = n0+n1+n2
    2. 度为1的节点有两条边,而度为2的节点有一条边,所以总边数 = n1+2*n2
    3. 除了根节点外,每个节点头上,都有一个边,所以总边数 = n-1
    4. 综上推断出n0 = n2+1
2.2 真二叉树(Proper Binary Tree)

所有节点的度都要么为 0,要么为 2

2.3 满二叉树(Full Binary Tree)
  1. 最后一层节点的度都为 0,其他节点的度都为 2
  2. 高度h = log2(n + 1)
  3. 总节点数n = 2^h − 1 = 2 ^ 0 + 2 ^1 + 2 ^2 + ⋯+ 2 ^(h−1)

在这里插入图片描述

2.4 完全二叉树(Complete Binary Tree)
  1. 对节点从上至下、左至右开始编号,其所有编号都能与相同高度的满二叉树中的编号对应在这里插入图片描述
  2. 假设完全二叉树的高度为 h( h ≥ 1 ),那么
    1. 至少2^(h − 1) 个节点 ( 2 ^0 + 2 ^1 + 2 ^2 + ⋯+ 2 ^h)
    2. 最多有2^h-1个节点
    3. 因此高度h = floor(log2(n))+1,floor为向下取整、ceiling为向上取整
  3. 从上到下、从左到右对节点从 0 开始进行编号,对任意第 i 个节点
    1. 如果 i = 0 ,它是根节点
    2. 如果 i > 0 ,它的父节点编号为 floor( (i-1) / 2 )
    3. 如果 2i+1 ≤ n-1 ,它的左子节点编号为 2i+1,如果 2i+1 > n-1 ,它无左子节点
    4. 如果 2i + 2 ≤ n -1,它的右子节点编号为 2i + 2,如果 2i + 2 > n-1 ,它无右子节点
  4. 度为1的节点要么是1个,要么是0个
  5. 叶子节点个数n0 = floor((n+1)/2)
  6. 非叶子节点个数n1+n2 = floor(n/2)
2.5 二叉树的遍历
  1. 前序遍历(Preorder Traversal):根节点、前序遍历左子树、前序遍历右子树,适用于树状结构展示(注意左右子树的顺序)
    1. 通过观察,访问过程中,可以先拿到5,再拿到3的,但真正访问,确实先访问3再访问5,所以考虑使用栈来实现
    2. 使用栈实现
      1. 先访问根节点
      2. 将当前访问的节点的右节点入栈
      3. 继续访问左节点,如果左节点为null,说明左子树访问完毕,应开始访问右子树,因此出栈一个节点,作为新的节点
      4. 循环执行直到左节点为null,并且栈中也没有元素时结束
    3. 使用递归实现
//使用栈实现
public void preorder2(Visitor<E> visitor) {
	if (visitor == null || root == null) return;
	Node<E> node = root;
	Stack<Node<E>> stack = new Stack<>();
	while (true) {
		if (node != null) {
			// 访问node节点
			if (visitor.visit(node.element)) return;
			// 将右子节点入栈
			if (node.right != null) {
				stack.push(node.right);
			}
			// 向左走
			node = node.left;
		} else if (stack.isEmpty()) {
			return;
		} else { 
			// 处理右边
			node = stack.pop();
		}
	}
}

在这里插入图片描述

  1. 中序遍历(Inorder Traversal):中序遍历左子树、根节点、中序遍历右子树,适用于二叉搜索树的中序遍历按升序或者降序处理节点
    1. 通过观察,访问过程中,可以先拿到4,再拿到2的,但真正访问,确实先访问2再访问4,所以考虑使用栈来实现
    2. 使用栈实现
      1. 先入栈,再向左走
      2. 直到左边没有节点,出栈,访问,如果有节点存在,对其中序遍历,如果不存在继续出栈,直到栈中元素为空
//使用栈实现
public void inorder(Visitor<E> visitor) {
	if (visitor == null || root == null) return;
	Node<E> node = root;
	Stack<Node<E>> stack = new Stack<>();
	while (true) {
		if (node != null) {
			stack.push(node);
			// 向左走
			node = node.left;
		} else if (stack.isEmpty()) {
			return;
		} else {
			node = stack.pop();
			// 访问node节点
			if (visitor.visit(node.element)) return;
			// 让右节点进行中序遍历
			node = node.right;
		}
	}
}

在这里插入图片描述
4. 后序遍历(Postorder Traversal):后序遍历左子树、后序遍历右子树、根节点,适用于一些先子后父的操作
1. 使用栈实现:
1. 将根节点入栈
2. 循环执行以下操作,直到栈为空
1. 如果栈顶节点为叶子节点(1),或者上一次访问的节点是栈顶节点的子节点(先访问了3后访问2)
2. 弹出栈顶节点,进行访问
3. 否则 将栈顶节点的right、left按顺序入栈

public void postorder(Visitor<E> visitor) {
	if (visitor == null || root == null) return;
	// 记录上一次弹出访问的节点
	Node<E> prev = null;
	Stack<Node<E>> stack = new Stack<>();
	stack.push(root);
	while (!stack.isEmpty()) {
		Node<E> top = stack.peek();
		if (top.isLeaf() || (prev != null && prev.parent == top)) {
			prev = stack.pop();
			// 访问节点
			if (visitor.visit(prev.element)) return;
		} else {
			if (top.right != null) {
				stack.push(top.right);
			}
			if (top.left != null) {
				stack.push(top.left);
			}
		}
	}
}

在这里插入图片描述
5. 层序遍历(Level Order Traversal):从上到下、从左到右依次访问每一个节点
1. 使用队列
1. 根节点入队
2. 根节点出队、左子节点入队、右子节点入队
3. 左子节点X出队,X的左子节点入队、右子节点入队
2. 适用于
1. 计算二叉树的高度
2. 判断一棵树是否为完全二叉树
在这里插入图片描述

3 前驱节点与后继节点
3.1 前驱节点
  1. 中序遍历时的前一个节点
  2. 如果是二叉搜索树,前驱节点就是前一个比它小的节点
  3. 获取前驱节点时,分为三种情况
    1. node.left != null:例如6、13、8
      1. predecessor = node.left.right.right.right…,直到right为null
    2. node.left = null && node.parent != null:例如7、11、9、1
      1. predecessor = node.parent.parent.parent…,直到node在parent的右子树中
    3. nodeleft = null && node.parent = null:例如没有左子树的根节点
      1. 没有前驱节点
3.2 后继节点

与前驱节点完全相反

4 二叉搜索树
4.1 产生原因
  1. 在 n 个动态的整数中搜索某个整数,假设使用动态数组存放元素,从第 0 个位置开始遍历搜索,平均时间复杂度:O(n)
  2. 如果维护一个有序的动态数组,使用二分搜索,最坏时间复杂度:O(logn),但是添加、删除的平均时间复杂度是 O(n)
  3. 因此产生二叉搜索树,添加、删除、搜索的最坏时间复杂度均可优化至:O(logn)
4.2 性质
  1. 二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST
  2. 又被称为:二叉查找树、二叉排序树
  3. 任意一个节点的值都大于其左子树所有节点的值
  4. 任意一个节点的值都小于其右子树所有节点的值
  5. 二叉搜索树存储的元素必须具备可比较性,如果是自定义类型,需要指定比较方式
4.3 成员变量
  1. element:存放具体元素
  2. parent:存放指向父节点的指针
  3. left:指向左子节点
  4. right:指向右子节点
4.4 添加方法
  1. 找到父节点
  2. 使用父节点来创建新节点
  3. parent.left = node 或者 parent.right = node
  4. 遇到相等的元素,可以考虑用新值覆盖旧值
4.5 元素间比较方案
  1. 允许外界传入一个 Comparator 自定义比较方案
  2. 如果没有传入 Comparator,强制认定元素实现了 Comparable 接口
4.6 打印二叉树
  1. 网站
https://github.com/CoderMJLee/BinaryTrees
  1. 使用方法
    1. 自定义的二叉树实现BinaryTreeInfo接口
    2. 重写该接口中root、left、right、string接口
    3. 客户端代码中可以通过BinaryTrees.println(bst);进行打印
4.7 删除节点
  1. 要删除的节点为叶子节点:直接删除
    1. 判断该节点是其父节点的左子节点还是右子节点
    2. 如果是右子节点:node.parent.right = null
    3. 如果是左子节点:node.parent.left = null
    4. 如果压根没有父节点,说明正在删除根节点:root = null
  2. 删除度为1的节点:用该节点的子节点替代该节点
    1. 如果其子节点在右侧:node.parent.left = child
    2. 如果其子节点在左侧: node.parent.right = child
    3. 如果父节点为空,表示为根节点:root = child
  3. 删除度为2 的节点:用该节点的前驱节点或后继节点覆盖该节点,然后删除前驱、后继节点
    1. 如果一个节点的度为2,其前驱节点或后继节点的度只可能是1或0
4.8 代码
  1. BinaryTree
package com.mj.tree;

import java.util.LinkedList;
import java.util.Queue;

import com.mj.printer.BinaryTreeInfo;

@SuppressWarnings("unchecked")
public class BinaryTree<E> implements BinaryTreeInfo {
	protected int size;
	protected Node<E> root;
	
	public int size() {
		return size;
	}

	public boolean isEmpty() {
		return size == 0;
	}

	public void clear() {
		root = null;
		size = 0;
	}
	
	public void preorder(Visitor<E> visitor) {
		if (visitor == null) return;
		preorder(root, visitor);
	}
	
	private void preorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop) return;
		
		visitor.stop = visitor.visit(node.element);
		preorder(node.left, visitor);
		preorder(node.right, visitor);
	}
	
	public void inorder(Visitor<E> visitor) {
		if (visitor == null) return;
		inorder(root, visitor);
	}
	
	private void inorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop) return;
		
		inorder(node.left, visitor);
		if (visitor.stop) return;
		visitor.stop = visitor.visit(node.element);
		inorder(node.right, visitor);
	}
	
	public void postorder(Visitor<E> visitor) {
		if (visitor == null) return;
		postorder(root, visitor);
	}
	
	private void postorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop) return;
		
		postorder(node.left, visitor);
		postorder(node.right, visitor);
		if (visitor.stop) return;
		visitor.stop = visitor.visit(node.element);
	}
	
	public void levelOrder(Visitor<E> visitor) {
		if (root == null || visitor == null) return;
		
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);
		
		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			if (visitor.visit(node.element)) return;
			
			if (node.left != null) {
				queue.offer(node.left);
			}
			
			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}
	
	public boolean isComplete() {
		if (root == null) return false;
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);
		
		boolean leaf = false;
		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			if (leaf && !node.isLeaf()) return false;

			if (node.left != null) {
				queue.offer(node.left);
			} else if (node.right != null) {
				return false;
			}
			
			if (node.right != null) {
				queue.offer(node.right);
			} else { // 后面遍历的节点都必须是叶子节点
				leaf = true;
			}
		}
		
		return true;
	}
	
	public int height() {
		if (root == null) return 0;
		
		// 树的高度
		int height = 0;
		// 存储着每一层的元素数量
		int levelSize = 1;
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);
		
		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			levelSize--;
			
			if (node.left != null) {
				queue.offer(node.left);
			}
			
			if (node.right != null) {
				queue.offer(node.right);
			}

			if (levelSize == 0) { // 意味着即将要访问下一层
				levelSize = queue.size();
				height++;
			}
		}
		
		return height;
	}
	
	public int height2() {
		return height(root);
	}
	
	private int height(Node<E> node) {
		if (node == null) return 0;
		return 1 + Math.max(height(node.left), height(node.right));
	}
	
	protected Node<E> createNode(E element, Node<E> parent) {
		return new Node<>(element, parent);
	}

	protected Node<E> predecessor(Node<E> node) {
		if (node == null) return null;
		
		// 前驱节点在左子树当中(left.right.right.right....)
		Node<E> p = node.left;
		if (p != null) {
			while (p.right != null) {
				p = p.right;
			}
			return p;
		}
		
		// 从父节点、祖父节点中寻找前驱节点
		while (node.parent != null && node == node.parent.left) {
			node = node.parent;
		}

		// node.parent == null
		// node == node.parent.right
		return node.parent;
	}
	
	protected Node<E> successor(Node<E> node) {
		if (node == null) return null;
		
		// 前驱节点在左子树当中(right.left.left.left....)
		Node<E> p = node.right;
		if (p != null) {
			while (p.left != null) {
				p = p.left;
			}
			return p;
		}
		
		// 从父节点、祖父节点中寻找前驱节点
		while (node.parent != null && node == node.parent.right) {
			node = node.parent;
		}

		return node.parent;
	}

	public static abstract class Visitor<E> {
		boolean stop;
		/**
		 * @return 如果返回true,就代表停止遍历
		 */
		abstract boolean visit(E element);
	}
	
	protected static class Node<E> {
		E element;
		Node<E> left;
		Node<E> right;
		Node<E> parent;
		public Node(E element, Node<E> parent) {
			this.element = element;
			this.parent = parent;
		}
		
		public boolean isLeaf() {
			return left == null && right == null;
		}
		
		public boolean hasTwoChildren() {
			return left != null && right != null;
		}
		
		public boolean isLeftChild() {
			return parent != null && this == parent.left;
		}
		
		public boolean isRightChild() {
			return parent != null && this == parent.right;
		}
		
		public Node<E> sibling() {
			if (isLeftChild()) {
				return parent.right;
			}
			
			if (isRightChild()) {
				return parent.left;
			}
			
			return null;
		}
	}

	@Override
	public Object root() {
		return root;
	}

	@Override
	public Object left(Object node) {
		return ((Node<E>)node).left;
	}

	@Override
	public Object right(Object node) {
		return ((Node<E>)node).right;
	}

	@Override
	public Object string(Object node) {
		return node;
	}
}

  1. BST
package com.mj.tree;

import java.util.Comparator;

@SuppressWarnings("unchecked")
public class BST<E> extends BinaryTree<E> {
	private Comparator<E> comparator;
	
	public BST() {
		this(null);
	}
	
	public BST(Comparator<E> comparator) {
		this.comparator = comparator;
	}

	public void add(E element) {
		elementNotNullCheck(element);
		
		// 添加第一个节点
		if (root == null) {
			root = createNode(element, null);
			size++;

			// 新添加节点之后的处理
			afterAdd(root);
			return;
		}
		
		// 添加的不是第一个节点
		// 找到父节点
		Node<E> parent = root;
		Node<E> node = root;
		int cmp = 0;
		do {
			cmp = compare(element, node.element);
			parent = node;
			if (cmp > 0) {
				node = node.right;
			} else if (cmp < 0) {
				node = node.left;
			} else { // 相等
				node.element = element;
				return;
			}
		} while (node != null);

		// 看看插入到父节点的哪个位置
		Node<E> newNode = createNode(element, parent);
		if (cmp > 0) {
			parent.right = newNode;
		} else {
			parent.left = newNode;
		}
		size++;
		
		// 新添加节点之后的处理
		afterAdd(newNode);
	}
	
	/**
	 * 添加node之后的调整
	 * @param node 新添加的节点
	 */
	protected void afterAdd(Node<E> node) { }
	
	/**
	 * 删除node之后的调整
	 * @param node 被删除的节点
	 */
	protected void afterRemove(Node<E> node) { }

	public void remove(E element) {
		remove(node(element));
	}

	public boolean contains(E element) {
		return node(element) != null;
	}
	
	private void remove(Node<E> node) {
		if (node == null) return;
		
		size--;
		
		if (node.hasTwoChildren()) { // 度为2的节点
			// 找到后继节点
			Node<E> s = successor(node);
			// 用后继节点的值覆盖度为2的节点的值
			node.element = s.element;
			// 删除后继节点
			node = s;
		}
		
		// 删除node节点(node的度必然是1或者0)
		Node<E> replacement = node.left != null ? node.left : node.right;
		
		if (replacement != null) { // node是度为1的节点
			// 更改parent
			replacement.parent = node.parent;
			// 更改parent的left、right的指向
			if (node.parent == null) { // node是度为1的节点并且是根节点
				root = replacement;
			} else if (node == node.parent.left) {
				node.parent.left = replacement;
			} else { // node == node.parent.right
				node.parent.right = replacement;
			}
			
			// 删除节点之后的处理
			afterRemove(node);
		} else if (node.parent == null) { // node是叶子节点并且是根节点
			root = null;
			
			// 删除节点之后的处理
			afterRemove(node);
		} else { // node是叶子节点,但不是根节点
			if (node == node.parent.left) {
				node.parent.left = null;
			} else { // node == node.parent.right
				node.parent.right = null;
			}
			
			// 删除节点之后的处理
			afterRemove(node);
		}
	}
	
	private Node<E> node(E element) {
		Node<E> node = root;
		while (node != null) {
			int cmp = compare(element, node.element);
			if (cmp == 0) return node;
			if (cmp > 0) {
				node = node.right;
			} else { // cmp < 0
				node = node.left;
			}
		}
		return null;
	}
	
	/**
	 * @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
	 */
	private int compare(E e1, E e2) {
		if (comparator != null) {
			return comparator.compare(e1, e2);
		}
		return ((Comparable<E>)e1).compareTo(e2);
	}
	
	private void elementNotNullCheck(E element) {
		if (element == null) {
			throw new IllegalArgumentException("element must not be null");
		}
	}
}

5 平衡二叉搜索树(Balanced Binary Search Tree)BBST
  1. 当节点数量固定时,左右子树的高度越接近,这棵二叉树就越平衡(高度越低)
  2. 最理想的平衡,就是像完全二叉树、满二叉树那样,高度是最小的
  3. 改进二叉搜索树:在节点的添加、删除操作之后,想办法让二叉搜索树恢复平衡(减小树的高度)
    1. 比较合理的改进方案是:用尽量少的调整次数达到适度平衡即可
    2. 一棵达到适度平衡的二叉搜索树,可以称之为:平衡二叉搜索树
  4. 经典常见的平衡二叉搜索树有
    1. AVL树
    2. 红黑树
  5. 他们一般也称为自平衡的二叉搜索树(Self-balancing Binary Search Tree)
6 AVL树
  1. 平衡因子(Balance Factor):也就是使其平衡的因素,为某结点的左右子树的高度差
  2. AVL树特点:每个节点的平衡因子只可能是 1、0、-1(绝对值 ≤ 1,如果超过 1,称之为“失衡”)
  3. AVL树的AVLNode多了一个表示高度的height,默认高度为1
6.1 添加导致的失衡处理
  1. 在添加方法最后,增加一个afterAdd方法,用于更新高度变化了的节点的高度,并回复平衡
  2. 可以想象,添加会导致其父节点和祖先节点高度变化,因此添加不会导致父节点和非祖先节点失衡
  3. 只要回复第一个不平衡的节点,所有的不平衡节点就都恢复平衡
  4. 循环找新增加节点的父节点
    1. 如果平衡,更新该节点高度(左右子节点中最高的高度+1)
    2. 如果不平衡,恢复平衡,并退出循环
      1. 根据第一个不平衡的节点grand、其最高的子节点parent、parent最高的子节点node的位置情况,将不平衡分为四种不同
      2. RR:g右边是p,p右边是n,让g左旋转
      3. LL:g左边是p,p左边是n,让g右旋转
      4. LR:g左边是p,p右边是n,先让p左旋转变为LL,再让g右旋转
      5. RL:g右边是p,p左边是n,先让p右旋转变为RR,再让g左旋转
  5. 旋转的本质(左旋转为例)
    1. 当需要对grand节点旋转时,需要先获取grand节点的右边节点parent,因为左旋转,一定是围绕着右子节点旋转
    2. 然后要获得右子节点的左子节点child,因为这个节点要改变位置
    3. 让grand的左侧指向child
    4. 让parent的右侧指向grand
    5. 更新parent和高度
  6. 更新parent和高度的本质
    1. 当grand旋转后,需要更新grand、parent、child三者的parent属性,因为他们的parent都发生了变化
    2. 同时grand和parent的高度会发生变化,所以需要更新他们的高度
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
6.2 删除导致的失衡处理
  1. 删除导致失衡,一定是删除了某个节点A的子树中,高度最低的那个中的节点,但很明显这不会改变该A节点的高度,因此删除只会导致父节点或祖先节点中的某一个节点失衡,其他节点都不会失衡
  2. 删除时,为了调整平衡,也会进行旋转,旋转会降低较高子树的高度,因此可能导致更高层的祖先节点失衡,需要再次调整平衡
  3. 极端情况下,所有祖先节点都需要进行恢复平衡的操作,共 O(logn) 次调整
  4. afterRemove和afterAdd相比,只是去掉了恢复平衡后的退出循环即可
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
6.3 总结
  1. 搜索:O(logn)
  2. 添加:O(logn),仅需 O(1) 次的旋转操作
  3. 删除:O(logn),最多需要 O(logn) 次的旋转操作
6.4 代码
package com.mj.tree;

import java.util.Comparator;

public class AVLTree<E> extends BST<E> {
	public AVLTree() {
		this(null);
	}
	
	public AVLTree(Comparator<E> comparator) {
		super(comparator);
	}
	
	@Override
	protected void afterAdd(Node<E> node) {
		while ((node = node.parent) != null) {
			if (isBalanced(node)) {
				// 更新高度
				updateHeight(node);
			} else {
				// 恢复平衡
				rebalance(node);
				// 整棵树恢复平衡
				break;
			}
		}
	}
	
	@Override
	protected void afterRemove(Node<E> node) {
		while ((node = node.parent) != null) {
			if (isBalanced(node)) {
				// 更新高度
				updateHeight(node);
			} else {
				// 恢复平衡
				rebalance(node);
			}
		}
	}
	
	@Override
	protected Node<E> createNode(E element, Node<E> parent) {
		return new AVLNode<>(element, parent);
	}
	
	/**
	 * 恢复平衡
	 * @param grand 高度最低的那个不平衡节点
	 */
	@SuppressWarnings("unused")
	private void rebalance2(Node<E> grand) {
		Node<E> parent = ((AVLNode<E>)grand).tallerChild();
		Node<E> node = ((AVLNode<E>)parent).tallerChild();
		if (parent.isLeftChild()) { // L
			if (node.isLeftChild()) { // LL
				rotateRight(grand);
			} else { // LR
				rotateLeft(parent);
				rotateRight(grand);
			}
		} else { // R
			if (node.isLeftChild()) { // RL
				rotateRight(parent);
				rotateLeft(grand);
			} else { // RR
				rotateLeft(grand);
			}
		}
	}
	/**
	 * 恢复平衡
	 * @param grand 高度最低的那个不平衡节点
	 */
	private void rebalance(Node<E> grand) {
		Node<E> parent = ((AVLNode<E>)grand).tallerChild();
		Node<E> node = ((AVLNode<E>)parent).tallerChild();
		if (parent.isLeftChild()) { // L
			if (node.isLeftChild()) { // LL
				rotate(grand, node, node.right, parent, parent.right, grand);
			} else { // LR
				rotate(grand, parent, node.left, node, node.right, grand);
			}
		} else { // R
			if (node.isLeftChild()) { // RL
				rotate(grand, grand, node.left, node, node.right, parent);
			} else { // RR
				rotate(grand, grand, parent.left, parent, node.left, node);
			}
		}
	}
	
	private void rotate(
			Node<E> r, // 子树的根节点
			Node<E> b, Node<E> c,
			Node<E> d,
			Node<E> e, Node<E> f) {
		// 让d成为这棵子树的根节点
		d.parent = r.parent;
		if (r.isLeftChild()) {
			r.parent.left = d;
		} else if (r.isRightChild()) {
			r.parent.right = d;
		} else {
			root = d;
		}
		
		//b-c
		b.right = c;
		if (c != null) {
			c.parent = b;
		}
		updateHeight(b);
		
		// e-f
		f.left = e;
		if (e != null) {
			e.parent = f;
		}
		updateHeight(f);
		
		// b-d-f
		d.left = b;
		d.right = f;
		b.parent = d;
		f.parent = d;
		updateHeight(d);
	}
	
	private void rotateLeft(Node<E> grand) {
		Node<E> parent = grand.right;
		Node<E> child = parent.left;
		grand.right = child;
		parent.left = grand;
		afterRotate(grand, parent, child);
	}
	
	private void rotateRight(Node<E> grand) {
		Node<E> parent = grand.left;
		Node<E> child = parent.right;
		grand.left = child;
		parent.right = grand;
		afterRotate(grand, parent, child);
	}
	
	private void afterRotate(Node<E> grand, Node<E> parent, Node<E> child) {
		// 让parent称为子树的根节点
		parent.parent = grand.parent;
		if (grand.isLeftChild()) {
			grand.parent.left = parent;
		} else if (grand.isRightChild()) {
			grand.parent.right = parent;
		} else { // grand是root节点
			root = parent;
		}
		
		// 更新child的parent
		if (child != null) {
			child.parent = grand;
		}
		
		// 更新grand的parent
		grand.parent = parent;
		
		// 更新高度
		// 因为平衡的时候,高度已经更新了,此处旋转后高度变化,而其高度还未更新,所以需要重新更新高度
		updateHeight(grand);
		updateHeight(parent);
	}
	
	private boolean isBalanced(Node<E> node) {
		return Math.abs(((AVLNode<E>)node).balanceFactor()) <= 1;
	}
	
	private void updateHeight(Node<E> node) {
		((AVLNode<E>)node).updateHeight();
	}
	
	private static class AVLNode<E> extends Node<E> {
		int height = 1;
		
		public AVLNode(E element, Node<E> parent) {
			super(element, parent);
		}
		
		public int balanceFactor() {
			int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
			int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
			return leftHeight - rightHeight;
		}
		
		public void updateHeight() {
			int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
			int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
			height = 1 + Math.max(leftHeight, rightHeight);
		}
		
		public Node<E> tallerChild() {
			int leftHeight = left == null ? 0 : ((AVLNode<E>)left).height;
			int rightHeight = right == null ? 0 : ((AVLNode<E>)right).height;
			if (leftHeight > rightHeight) return left;
			if (leftHeight < rightHeight) return right;
			return isLeftChild() ? left : right;
		}
		
		@Override
		public String toString() {
			String parentString = "null";
			if (parent != null) {
				parentString = parent.element.toString();
			}
			return element + "_p(" + parentString + ")_h(" + height + ")";
		}
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值