红黑树
红黑树首先是一种树形结构,同时又是一个二叉树(每个节点最多只能有两个孩子节点,左节点小于等于父节点,右节点大于父节点),为了保证树的左右孩子树相对平衡(深度相同),红黑树使用了节点标色的方式,将节点标记为红色或者黑色,在计算树的深度时只统计黑色节点的数量,不统计红色节点数量,同时没有变量高度,增加了父亲节点、叔叔节点颜色变量等。
主要特点(规则)
在进行插入和删除时有可能会触发红黑树的插入平衡调整(balanceInsertion方法)或删除平衡调整(balanceDeletion )方法,调整的方式主要有以下手段:左旋转(rotateLeft方法)、右旋转(rotateRight方法)、颜色反转,进行调整的原因是为了维持红黑树的数据结构。
代码实现
声明变量
package edu.xalead.tree.redTree;
import com.sun.org.apache.regexp.internal.RE;
import sun.reflect.generics.tree.Tree;
public class TreeNode {
public static Boolean BLACK = false;//黑色
public static Boolean RED = true;//红色
private Object data = null;
private TreeNode left = null;
private TreeNode right = null;
private Boolean color = RED;
private TreeNode parent;
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
public Boolean getColor() {
return color;
}
public void setColor(Boolean color) {
this.color = color;
}
public TreeNode(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
添加数字所需要的接口
package edu.xalead.tree.redTree;
public interface Comp {
/**
* 我的规范:只要想往我的BStree树中添加数据,就要实现这个方法,比较大小
* @param p 要跟自己比较的数据
* @return 0 相等 1 自己 大于 参数 -1 自己 小于 参数
*/
public abstract int compare(Object p);
}
比较的实体对象 这里主要是比较年龄
package edu.xalead.tree.redTree;
public class User implements Comp{
private int id;
private int age;
private String name;
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compare(Object p) {
if(!(p instanceof User)) return -1;
User u = (User)p;
if(this.getAge() > u.age) return 1;
else if(this.getAge() < u.age) return -1;
return 0;
}
}
调整树高度的主方法
package edu.xalead.tree.redTree;
public class RBTree {
private TreeNode root = null;
private int size = 0;
/**
*
* @param currentnode 当前准备往左或右添加子节点的节点
* @param newNode 准备添加的新节点
*/
private TreeNode f(TreeNode currentnode,TreeNode newNode){
Comp c = (Comp) currentnode.getData();
Comp n = (Comp) newNode.getData();
if(c.compare(n) == -1){
if(currentnode.getRight() != null){
f(currentnode.getRight(),newNode);
}else{
//右页子节点插入
currentnode.setRight(newNode);
newNode.setParent(currentnode);
insertFixUp(newNode);
}
}else if(c.compare(n) == 1){
if(currentnode.getLeft() != null){
f(currentnode.getLeft(),newNode);
}else{
//左页子节点插入
currentnode.setLeft(newNode);
newNode.setParent(currentnode);
insertFixUp(newNode);
}
}else {
return currentnode;
}
return currentnode;
}
//调整红黑树
private void insertFixUp(TreeNode node) {
TreeNode parent,gparent;
// 父节点不为空并且父节点是红色时
while((parent = parent(node)) != null && isRed(parent)){
gparent = parent(parent);//爷爷节点
//如果父节点是爷爷节点的左孩子
if(parent == gparent.getLeft()){
//叔叔节点是不是红色
TreeNode uncle = gparent.getRight();
if(uncle != null && isRed(uncle)){
uncle.setColor(TreeNode.BLACK);
parent.setColor(TreeNode.BLACK);
gparent.setColor(TreeNode.RED);
node = gparent; // 爷爷节点作为当前节点
continue;
}
//叔叔是黑色,且当前节点是右孩子 左旋
if(parent.getRight() == node){
TreeNode temp = parent;
left_rotate(parent);
parent = node;
node = temp;
}
//叔叔是黑色,且当前节点是左孩子 右旋
parent.setColor(TreeNode.BLACK);
gparent.setColor(TreeNode.RED);
right_rotate(gparent);
}else{//如果父节点是爷爷节点的右孩子
//叔叔节点是红色
TreeNode uncle = gparent.getLeft();
if(uncle != null && isRed(uncle)){
uncle.setColor(TreeNode.BLACK);
parent.setColor(TreeNode.BLACK);
gparent.setColor(TreeNode.RED);
node = gparent;
continue;
}
//叔叔是黑色,且当前节点是左孩子 右旋
if(parent.getLeft() == node){
TreeNode temp = parent;
right_rotate(parent);
parent = node;
node = temp;
}
//叔叔是黑色,且当前节点是左孩子 左旋
parent.setColor(TreeNode.BLACK);
gparent.setColor(TreeNode.RED);
left_rotate(gparent);
}
}
// if(this.root.getColor() == TreeNode.RED)
this.root.setColor(TreeNode.BLACK);
}
private boolean isRed(TreeNode parent) {
if(parent == null || parent.getColor() == TreeNode.BLACK)
return false;
return true;
}
private boolean isBlack(TreeNode parent) {
if(parent == null || parent.getColor() == TreeNode.BLACK)
return true;
return false;
}
private TreeNode parent(TreeNode node) {
return node.getParent() == null ? null : node.getParent();
}
/**
* 左旋转
* a b
* / / \
* b c a
* /
* c
* @param
*/
public TreeNode left_rotate(TreeNode g){
TreeNode p = g.getRight();
g.setRight(p.getLeft());
if(p.getLeft() != null){
p.getLeft().setParent(g);
}
// 把g的父节点设为p的父节点
p.setParent(g.getParent());
if(g.getParent() == null){
this.root = p;
}else{
if(g.getParent().getLeft() == g){
g.getParent().setLeft(p);
}else{
g.getParent().setRight(p);
}
}
//把爷节点设为父节点的左孩子
p.setLeft(g);
g.setParent(p);
return p;
}
/**
* 右旋转
* @param g
* @return
*/
public TreeNode right_rotate(TreeNode g){
TreeNode p = g.getLeft();
g.setLeft(p.getRight());
if(p.getRight() != null){
p.getRight().setParent(g);
}
// 把g爷节点父节点设为p
p.setParent(g.getParent());
if(g.getParent() == null){
this.root = p;
}else{
if(g.getParent().getRight() == g){
g.getParent().setRight(p);
}else{
g.getParent().setLeft(p);
}
}
//把爷节点设为父节点的右孩子
p.setRight(g);
g.setParent(p);
return p;
}
public void add(Comp data){
TreeNode node = new TreeNode(data);
if(root == null){
node.setColor(TreeNode.BLACK);
root = node;
}
else{
f(this.root,node);
}
this.size++;
}
public int size(){
return this.size;
}
}
测试
package edu.xalead.tree.redTree;
public class Test {
public static void main(String[] args) {
RBTree bstree = new RBTree();
//添加树后左旋
// bstree.add(new User(1111,20,"张三"));
// bstree.add(new User(1112,24,"李四"));
// bstree.add(new User(1112,28,"王麻子"));
// bstree.add(new User(1113,49,"赵六"));
// bstree.add(new User(1114,77,"马七"));
// 添加树后右旋
bstree.add(new User(1111,50,"张三"));
bstree.add(new User(1112,45,"李四"));
bstree.add(new User(1112,42,"王麻子"));
bstree.add(new User(1113,30,"赵六"));
bstree.add(new User(1114,35,"马七"));
System.out.println(bstree.size());
}
}
结果展示: 左旋
右旋
想要对红黑树原理进行深切的理解,可以通过调试跟踪节点之间的联系与变化,也可以在https://www.cs.usfca.edu/~galles/visualization/Algorithms.html网站进行验证和学习。