2-3-4树(引出红黑树)
模型
- 在普通的二叉查找树上进行扩展,他允许有多个键(1–3个 --> 对应2-node,3-node,4-node)
- 树保持完美平衡。(根到每个叶子结点的路径都是一样长的)
特点
动态 添加 和 删除 时能保持完美平衡。
插入
当目标结点是4-node则需要分裂。
分为两种分裂方式:
Bottom-up 自底向上(到目标结点,再一步一步向上分裂)
Top-down 自顶向下(沿着查找路径向下寻找,遇到4-node结点就分裂4-node,确保当前的结点不是4-node,给目标位置新结点的分裂留下空间(使其父结点不是4-node))
这些变换都是局部变换。不影响其他无关的结点。
性能
只与树的高度有关:
红黑树(2-3-4的树简单实现)
- 用BST来表示2-3-4树
- 用“内部的”红色边表示3-node和4-node
- 2-3-4树能表示成BST,它们之间有一种对应关系,但这种对应关系不是1对1的。2-node和4-node是唯一的,但是3-node不唯一,要考虑的情况就会变多。
定义
- 每个结点(子结点)或者是红色的,或者是黑色的
- 根结点是黑色的
- 叶结点(Nil)是黑色的
- 如果一个结点是红色的,则它的两个子结点是黑色的(4-node只有一种编码方式)
- 对每个结点,从该结点到所有后代叶结点的简单路径上,均包含相同数目的黑色结点。(黑高平衡)
Left-leaning red-black trees(左倾红黑树)
- 用BST来表现2-3-4树
- 用“内部的”红色边表示3-node和4-node
- 3-node的红色边是左倾的
实现
JDK实现普通的红黑树,非LLRB。
局部变换----旋转
package day14;
import java.util.*;
public class RedBlackTree<K extends Comparable<? super K>,V> {
//颜色常量
private static final boolean RED = true;
private static final boolean BLACK = false;
//属性
private TreeNode root;
private class TreeNode {
K key;
V value;
TreeNode left;
TreeNode right;
boolean color;
int size; // 以这个结点为根的树的键值对个数
public TreeNode(K key, V value, boolean color, int size) {
this.key = key;
this.value = value;
this.color = color;
this.size = size;
}
}
/***********************************************************
* unordered methods
**********************************************************/
/**
* 插入键值对
* @param key 键
* @param value 值
*/
public void put(K key, V value) {
if (key == null || value == null) {
throw new IllegalArgumentException("Key or value cannot be null.");
}
root = put(root, key, value);
root.color = BLACK; // if添加的是第一个结点 或者 分解根结点 都会导致根结点变成红色的,所以要把它变黑。
check();
}
// 对应2-3树
private TreeNode put(TreeNode x, K key, V value) {
// 底部添加结点的情形
if (x == null) return new TreeNode(key, value, RED, 1);
// 自顶向下分解4-node
/* if (isRed(x.left) && isRed(x.right)) { // 如果放在修复阶段,则RBT不会存在4-node
flipColors(x);
}*/
int cmp = key.compareTo(x.key);
if (cmp < 0) x.left = put(x.left, key, value);
else if (cmp > 0) x.right = put(x.right, key, value);
else x.value = value;
//自底向上修复
return fixUp(x);
}
/**
* 获取键关联的值
* @param key 键
* @return 键关联的值,如果键不存在,返回null
*/
public V get(K key) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null");
}
TreeNode x = root;
while (x != null) {
int cmp = key.compareTo(x.key);
if (cmp < 0) x = x.left;
else if (cmp > 0) x = x.right;
else return x.value;
}
return null;
}
/**
* 判断键是否存在
* @param key 键
* @return 如果键存在返回true,否则返回false
*/
public boolean contains(K key) {
return get(key) != null;
}
/**
* 清空所有键值对
*/
public void clear() {
root = null;
}
/**
* 判断树是否为空
* @return 如果集合为空,返回true;否则返回false
*/
public boolean isEmpty() {
return root == null;
}
/**
* 获取键值对的个数
* @return 键值对的个数
*/
public int size() {
return size(root); // 避免空指针异常
}
public Set<K> keys() {
Set<K> set = new LinkedHashSet<>();
inOrder(root,set);
return set;
}
private void inOrder(TreeNode x, Set<K> set) {
if (x == null) return;
inOrder(x.left, set);
set.add(x.key);
inOrder(x.right, set);
}
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null");
}
if (!contains(key)) return ;
// 删除指定的键值对
if(!isRed(root.left)) root.color = RED;
root = delete(root, key);
if (!isEmpty()) {
root.color = BLACK;
}
check();
}
private TreeNode delete(TreeNode x, K key) {
if (key.compareTo(x.key) < 0) {
if (!isRed(x.left) && !isRed(x.left.left)) {
x = moveRedLeft(x);
}
x.left = delete(x.left, key);
} else {
// 为了方便删除,右旋左倾的红色链接
if (isRed(x.left)) x = rotateRight(x);
// 在底部删结点
if (key.compareTo(x.key) == 0 && x.right == null) {
return null;
}
// 如果右孩子是2-node,需要从左孩子中借结点
if (!isRed(x.right) && !isRed(x.right.left)) {
x = moveRedRight(x);
}
if (key.compareTo(x.key) == 0) {
TreeNode minOfRight = min(x.right);
x.key = minOfRight.key;
x.value = minOfRight.value;
x.right = deleteMin(x.right);
} else {
x.right = delete(x.right, key);
}
}
return fixUp(x);
}
/***********************************************************
* ordered methods
**********************************************************/
/**
* 获取键的最小值
* @return 键的最小值
*/
public K min() {
if(isEmpty()){
throw new NoSuchElementException("Tree is Empty!");
}
return min(root).key; //min(root)查找最小结点
}
// 调用这个方法时node不能为null
private TreeNode min (TreeNode node) {
TreeNode x = node;
while (x.left != null) x = x.left;
return x;
}
public K max() {
if(isEmpty()){
throw new NoSuchElementException("Tree is Empty!");
}
return max(root).key; //min(root)查找最大结点
}
private TreeNode max (TreeNode node) {
TreeNode x = node;
while (x.right != null) x = x.right;
return x;
}
public void deleteMax() {
if (isEmpty()) {
throw new NoSuchElementException("The tree is empty!");
}
if (!isRed(root.left)) root.color = RED; //保证这个结点是红色的。则前提条件得到了满足(moveRedRight)
root = deleteMax(root);
if (!isEmpty()) root.color = BLACK; //自底向上分解4-node,可能导致根结点变红,所以需要这一步
check();
}
private TreeNode deleteMax(TreeNode x) {
// 右旋左倾的红色链接
if (isRed(x.left)) {
x = rotateRight(x); //判断是否有左倾红色结点
}
if (x.right == null) return null; //判断右子树是否为null(因为完美平衡,所以右结点没有,左结点也没有,直接删除)
if(!isRed(x.right) && !isRed(x.right.left)) { //判断右孩子是否为2-node,是则从兄弟结点借结点。
x = moveRedRight(x);
}
x.right = deleteMax(x.right); // 递归删除右子树的最大值
return fixUp(x); // 自底向上修复
}
/**
* 删除最小值
*/
private void deleteMin() {
if (isEmpty()) {
throw new NoSuchElementException("The tree is Empty!");
}
if (!isRed(root.left)) root.color = RED; // 染红根结点
root = deleteMin(root);
if(!isEmpty()) root.color = BLACK;
check();
}
private TreeNode deleteMin(TreeNode x) {
if (x.left == null) return null; //底部删除最小值
if(!isRed(x.left) && !isRed(x.left.left)) {
x = moveRedLeft(x); //左孩子是2-node,从右孩子借结点
}
// 往左走
x.left = deleteMin(x.left);
return fixUp(x);
}
/**
* 获取小于等于指定值key的最大键
* @param key 指定值
* @return 小于等于指定值的最大键
*/
public K floor(K key) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null.");
}
TreeNode x = floor(root, key);
if (x == null) return null;
return x.key;
}
private TreeNode floor(TreeNode x, K key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) return floor(x.left, key);
// key > x.key,x就是备选
TreeNode t = floor(x.right, key);
if(t != null) return t;
return x;
}
/**
* 返回大于等于指定值key的最小键
* @param key 指定值
* @return 大于等于key的最小键
*/
private K ceiling(K key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null.");
}
TreeNode x = ceiling(root, key);
if (x == null) return null;
else return x.key;
}
private TreeNode ceiling(TreeNode x, K key) {
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp > 0) return ceiling(x.right, key);
TreeNode t = ceiling(x.left, key);
if (t != null) return t;
return x;
}
/**
* 获取键的排名(小于key的键的个数)
* @param key 键
* @return 键的排名
*/
public int rank(K key) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null.");
}
return rank(root, key);
}
private int rank(TreeNode x, K key) {
if (x == null) return 0;
int cmp = key.compareTo(x.key);
if (cmp == 0) return size(x.left);
if (cmp < 0) return rank(x.left, key);
return rank(x.right, key) + 1 + size(x.left);
}
/**
* 获取排名为k的键
* @param k 排名
* @return 排名为k的键
*/
public K select(int k) {
if (k < 0 || k >= size()) {
throw new IndexOutOfBoundsException("k = " + k + ", size = " + size());
}
return select(root, k).key;
}
private TreeNode select(TreeNode x, int k) {
int rank = size(x.left);
if (k == rank) return x;
if (k < rank) return select(x.left, k);
return select(x.right, k - rank - 1);
}
/**
* 获取大于等于low,小于等于high的键的个数
* @param low 下界
* @param high 上界
* @return 键的个数
*/
public int size(K low, K high) {
if (low == null || high == null) {
throw new IllegalArgumentException("Low or high cannot be null.");
}
if (low.compareTo(high) > 0) {
throw new IllegalArgumentException("Low cannot be larger than high.");
}
int r1 = rank(low); // 小于low的元素个数
int r2 = rank(high); // 小于high的元素个数
//[low, high]
if (contains(high)) return r2 - r1 + 1; //画轴
return r2 - r1;
}
/**
* 获取大于等于low,小于等于high的键的集合
* @param low 下界
* @param high 上界
* @return 键的集合
*/
public Set<K> keys(K low, K high) {
if (low == null || high == null) {
throw new IllegalArgumentException("Low or high cannot be null.");
}
if (low.compareTo(high) > 0) {
throw new IllegalArgumentException("Low cannot be larger than high.");
}
Set<K> set = new LinkedHashSet<>();
inOrder(root, low, high, set);
return set;
}
private void inOrder(TreeNode x, K low, K high, Set<K> set) {
if (x == null) return;
int cmp1 = low.compareTo(x.key);
int cmp2 = high.compareTo(x.key);
if (cmp1 < 0) inOrder(x.left, low, high, set); //遍历左子树(剪枝)
if (cmp1 <= 0 && cmp2 >= 0) set.add(x.key); //添加根结点
if (cmp2 > 0) inOrder(x.right, low, high, set); // 遍历右子树(剪枝)
}
/***********************************************************
* helper methods
**********************************************************/
private boolean isRed(TreeNode x) {
if (x == null) return false;
return x.color == RED;
}
private int size(TreeNode x) {
if (x == null) return 0;
return x.size;
}
//左旋,右旋
private TreeNode rotateLeft(TreeNode h) {
// 调整结构
TreeNode x =h.right;
h.right = x.left;
x.left = h;
// 调整颜色
x.color = x.left.color;
h.color = RED;
// 调整size
x.size = h.size;
h.size = size(h.left) + size(h.right) + 1; //防止子树为空
return x;
}
private TreeNode rotateRight(TreeNode h) {
TreeNode x = h.left;
h.left = x.right;
x.right = h;
x.color = x.right.color;
h.color = RED;
x.size = h.size;
h.size = size(h.left) + size(h.right) + 1;
return x;
}
public void flipColors(TreeNode x) {
x.color = !x.color;
x.left.color = !x.left.color;
x.right.color = !x.right.color;
}
private TreeNode fixUp (TreeNode x) {
x.size = size(x.left) + size(x.right) + 1; //修复,先该其size的值
if(isRed(x.right)) { // 判断有没有右倾红色结点
x = rotateLeft(x);
}
if(isRed(x.left) && isRed(x.left.left)) { //判断有没有不标准的4-node
x = rotateRight(x);
}
if (isRed(x.left) && isRed(x.right)) { // 删除4-node。RBT不会存在4-node
flipColors(x);
}
return x;
}
private TreeNode moveRedRight(TreeNode x) { //向左兄弟结点借结点
flipColors(x); //先变色
if(isRed(x.left.left)) { //再判断是否为4-node
x = rotateRight(x); //是的话,右转,再变色
flipColors(x);
}
return x;
}
private TreeNode moveRedLeft(TreeNode x) { //向右兄弟结点借结点
flipColors(x);
if(isRed(x.right.left)) {
x.right = rotateRight(x.right);
x = rotateLeft(x);
flipColors(x);
}
return x;
}
/***********************************************************
* check methods
**********************************************************/
private boolean check() {
boolean isBST = isBST();
if (!isBST) System.err.println("The tree is not a BST!");
boolean is23 = is23();
if (!is23) System.err.println("The tree is not a 2-3 tree!");
boolean isBalanced = isBalanced();
if(!isBalanced) System.err.println("The tree is not balanced!");
return isBST && is23 && isBalanced;
}
private boolean isBalanced() {
// 先求从根结点到某个叶子结点的路劲长
int height = 0;
TreeNode x = root;
while (x != null) {
if (!isRed(x)) height++;
x = x.left;
}
return isBalanced(root,height); // 判断从根结点到所有叶子结点的高度是不是等于height
}
private boolean isBalanced(TreeNode x, int height) {
if (x == null) return height == 0; // x为结点则说明为叶结点,此时如果传入的height = 0了,则说明高度相同
if (!isRed(x)) height--;
return isBalanced(x.left, height) && isBalanced(x.right, height);
}
private boolean is23() {
return !isRed(root) && is23(root);
}
private boolean is23(TreeNode x) {
if (x == null) return true;
if (isRed(x.right)) return false; //要求3-node必须左倾
if (isRed(x.left) && isRed(x.left.left)) return false;
return is23(x.left) && is23(x.right);
}
private boolean isBST() {
return isBST(root);
}
private boolean isBST(TreeNode x) {
if (x == null) return true;
if (x.left != null) {
TreeNode maxOfLeft = max(x.left);
if (x.key.compareTo(maxOfLeft.key) <= 0) return false;
}
if (x.right != null) {
TreeNode minOfRight = min(x.right);
if (x.key.compareTo(minOfRight.key) >= 0) return false;
}
return isBST(x.left) && isBST(x.right);
}
public static void main(String[] args) {
// void put(K key, V value)
RedBlackTree<Character, Object> tree = new RedBlackTree<>();
Object dummyValue = "刘亦菲";
tree.put('A', dummyValue);
tree.put('S', dummyValue);
tree.put('E', dummyValue);
tree.put('R', dummyValue);
tree.put('C', dummyValue);
tree.put('D', dummyValue);
tree.put('I', dummyValue);
tree.put('N', dummyValue);
tree.put('B', dummyValue);
tree.put('X', dummyValue);
// System.out.println(tree.check()); true
//void clear(), int size(), boolean isEmpty()
// System.out.println(tree.keys());
// System.out.println(tree.size());
// System.out.println(tree.isEmpty());
// tree.clear();
// System.out.println(tree.keys());
// System.out.println(tree.size());
// System.out.println(tree.isEmpty());
// V get(K key)
// System.out.println(tree.get('A'));
// System.out.println(tree.get('Z'));
// //boolean contains(K key)
// System.out.println(tree.contains('A'));
// System.out.println(tree.contains('Z'));
//K max() K min()
// System.out.println(tree.min());
// System.out.println(tree.max());
// tree.clear();
// System.out.println(tree.min());
// System.out.println(tree.max());
// void deleteMax()
// System.out.println(tree.keys());
// while(!tree.isEmpty()) {
// tree.deleteMax();
// System.out.println(tree.keys());
// }
// System.out.println(tree.keys());
// tree.delete('Z');
// System.out.println(tree.keys());
// tree.delete('I');
// System.out.println(tree.keys());
// Set<Character> set = tree.keys();
// List<Character> list = new ArrayList<>(set);
// Collections.shuffle(list); //打乱
// System.out.println(set);
// for (Character c : list) {
// System.out.println(c);
// tree.delete(c);
// System.out.println(tree.keys());
// }
// K floor(K key)
// System.out.println(tree.keys());
// System.out.println(tree.floor('I'));
// System.out.println(tree.floor('M'));
// System.out.println(tree.floor('0'));
//K ceiling(K key)
// System.out.println(tree.keys());
// System.out.println(tree.ceiling('I'));
// System.out.println(tree.ceiling('M'));
// System.out.println(tree.ceiling('Z'));
// int rank(K key)
// System.out.println(tree.keys());
// System.out.println(tree.rank('I'));
// System.out.println(tree.rank('M'));
// System.out.println(tree.rank('Z'));
// System.out.println(tree.rank('0'));
//K select(int K)
// System.out.println(tree.keys());
// System.out.println(tree.select(0));
// System.out.println(tree.select(5));
// System.out.println(tree.select(9));
// Set<K> keys(K low, K high)
System.out.println(tree.keys());
System.out.println(tree.keys('C', 'O'));
System.out.println(tree.keys('B', 'N'));
}
}
作业
package day15;
/*
给定一棵二叉树,判断它是不是镜像对称的。
比如:
1
/ \
2 2
/ \ / \
3 4 4 3
这是镜像对称的
1
/ \
2 2
\ \
3 3
这不是镜像对称的
*/
public class Ex1 {
public boolean isSymmetric(TreeNode x) {
return isSymmetric(x, x);
}
private class TreeNode {
TreeNode left;
TreeNode right;
int value;
public TreeNode(int value) {
this.value = value;
}
}
private boolean isSymmetric(TreeNode x1, TreeNode x2) {
if (x1 == null && x2 == null) return true;
if (x1 == null || x2 == null) return false;
return x1.value == x2.value && isSymmetric(x1.left, x2.right) && isSymmetric(x1.right, x2.left);
}
}
B树
红黑树就是3阶或者4阶B树