介绍
- 本文介绍红黑树的主要性质和功能,并动手用Java写一个粗糙版的红黑树。
- 红黑树是一个平衡且有序的二叉树,主要方法包括:
- 详细源码 :RedBlackTreeImpl 和 测试案例 、 测试案例2
- 在正式开始前,思考三个问题:
- 红黑树的节点每次插入的位置,在未进行颜色调整前,一定处在当前树的叶子位置?
- 每次插入节点后,调整完颜色后,跟节点一定是黑色的?
- JDK中的TreeMap是红黑树结构,并且具有去重功能,那么是否可以写一个只排序,不去重的红黑树?
正文
put
- 说明
- 在map 中插入key的节点 默认是不去重的,通过增加一个参数duplicate,true表示去重,因此方法签名为:public boolean put(boolean duplicate, int key, int value) ,
- 设计思路
- 第一步:找到插入节点的位置,并做插入操作(调整其父子节点的关系)
- 第二步:进行颜色调整,颜色调整主要看 叔叔节点的颜色 和 父节点于祖父节点的位置
- 红叔问题:更改叔叔节点、父节点、祖父节点颜色,并向上递归
- 黑叔问题:涉及到左旋右旋及颜色调整
- 主要源码
public boolean put(boolean duplicate, int key, int value) {
if (initValue == this.root.key) {
this.root.key = key;
this.root.value = value;
this.root.color = BLACK;
this.size++;
return true;
}
RedBlackNode point = new RedBlackNode(key, value);
RedBlackNode search = this.root;
RedBlackNode search_bef = this.root;
while (search != null) {
search_bef = search;
if (duplicate && search.key == key) {
search.value = value;
return true;
} else if (search.key < key) {
search = search.right;
} else {
search = search.left;
}
}
point.parent = search_bef;
if (search_bef.key < key) {
search_bef.right = point;
} else {
search_bef.left = point;
}
insertFixup(point);
this.size++;
return true;
}
private void insertFixup(RedBlackNode point) {
if (this.root == point.parent) {
this.root.color = BLACK;
return;
}
RedBlackNode uncle;
while (null != point.parent && RED.equals(point.parent.color)) {
uncle = (point.parent == point.parent.parent.left) ? point.parent.parent.right : point.parent.parent.left;
if (null != uncle && RED.equals(uncle.color)) {
uncle.color = BLACK;
point.parent.color = BLACK;
point.parent.parent.color = RED;
point = point.parent.parent;
if (this.root == point) {
this.root.color = BLACK;
return;
}
} else {
if (uncle == point.parent.parent.right) {
if (point == point.parent.right) {
rotateLeft(point.parent);
point = point.left;
}
point.parent.color = BLACK;
point.parent.parent.color = RED;
rotateRight(point.parent.parent);
} else {
if (point == point.parent.left) {
rotateRight(point.parent);
point = point.right;
}
point.parent.color = BLACK;
point.parent.parent.color = RED;
rotateLeft(point.parent.parent);
}
}
}
}
private void rotateRight(RedBlackNode point) {
RedBlackNode pleft = point.left;
point.left = pleft.right;
if (null != pleft.right) {
pleft.right.parent = point;
}
pleft.parent = point.parent;
if (null == point.parent) {
this.root = pleft;
} else if (point.parent.left == point) {
point.parent.left = pleft;
} else {
point.parent.right = pleft;
}
pleft.right = point;
point.parent = pleft;
}
remove
contain
- 说明:查找key值是否存在于红黑树中。
- 设计思路
- 主要源码
public boolean contain(int key) {
return contain(this.root, key);
}
private boolean contain(RedBlackNode node, int key) {
if (node == null) {
return false;
}
if (node.key == key) {
return true;
}
if (key < node.key) {
return contain(node.left, key);
} else {
return contain(node.right, key);
}
}
get
- 说明:根据Key值获取Value值
- 设计思路
- 通过递归的思路,分别在左孩子和右孩子中查询值,并返回
- 主要源码
public int get(int key) {
return get(this.root, key);
}
private int get(RedBlackNode node, int key) {
if (node == null) {
return keyNotFound;
}
if (node.key == key) {
return node.value;
}
if (key < node.key) {
return get(node.left, key);
} else {
return get(node.right, key);
}
}
getBelowKey
- 说明:获取map内比这个key值小的元素个数
- 设计思路
- 如果当前节点等于查询值,那么统计其左孩子的节点数量
- 如果当前节点小于查询值,那么递归其右孩子节点,但是加上其左孩子的节点值,再加一(其节点本身)
- 如果当前节点大于查询值,那么递归其左孩子节点
- 主要源码
public int getBelowKey(int key) {
if (!contain(key)) {
return 0;
}
RedBlackNode root = this.root;
int count = 0;
while (root != null) {
if (root.key == key) {
return count + countNodeNum(root.left);
} else if (root.key < key) {
count = 1 + count + countNodeNum(root.left);
root = root.right;
} else {
root = root.left;
}
}
return 0;
}
private int countNodeNum(RedBlackNode node) {
if (node == null) {
return 0;
}
int count = countNodeNum(node.left) + countNodeNum(node.right) + 1;
return count;
}
print
public void print(RedBlackNode root) {
if (root == null) {
return;
}
Queue<RedBlackNode> queue = new LinkedList<>();
queue.offer(root);
RedBlackNode front = root;
RedBlackNode tail = root;
while (!queue.isEmpty()) {
RedBlackNode binaryTree = queue.poll();
System.out.print(binaryTree.key + " ");
if (binaryTree.left != null) {
queue.offer(binaryTree.left);
tail = binaryTree.left;
}
if (binaryTree.right != null) {
queue.offer(binaryTree.right);
tail = binaryTree.right;
}
if (binaryTree == front) {
front = tail;
System.out.println();
}
}
}
toarray
- 说明:按照先序遍历的方式,输出红黑树
- 设计思路
- 主要源码
public int[] toarray() {
if (this.root.key == initValue) {
return new int[]{};
}
this.red2array = new ArrayList<>();
RedBlackTree2Array(this.root);
int[] array = red2array.stream().mapToInt(Integer::intValue).toArray();
return array;
}
private void RedBlackTree2Array(RedBlackNode node) {
if (node == null) {
return;
}
RedBlackTree2Array(node.left);
this.red2array.add(node.key);
RedBlackTree2Array(node.right);
}