一、红黑树(Red-Black Tree)
1、概述
红黑树是一种平衡二叉搜索树,其中每个节点都包含一个额外的信息位,表示节点的颜色。节点的颜色可以是红色或黑色,这取决于节点存储的位信息。
这些颜色用于确保树在插入和删除期间保持平衡。虽然树的平衡并不完美,但减少搜索时间并将其保持在 O(log n) 时间左右就足够了,其中 n 是树中元素的总数。红黑树树是鲁道夫拜耳于 1972 年发明的。
每棵红黑树都遵循的规则:
1、每个节点都有红色或黑色的颜色。
2、树的根总是黑色的。
3、没有两个相邻的红色节点(红色节点不能有红色父节点或红色子节点)。
4、从节点(包括根节点)到其任何后代 NULL 节点的每条路径都具有相同数量的黑色节点。
5、所有叶子节点都是黑色节点。
2、和AVL树相比
与红黑树相比,AVL 树更平衡,但它们在插入和删除过程中可能会导致更多的旋转。因此,如果您的应用程序涉及频繁的插入和删除,那么应该首选红黑树。如果插入和删除不那么频繁,而搜索是一种更频繁的操作,那么 AVL 树应该优先于红黑树。
3、红黑树如何平衡
(1)重新着色
重新着色是节点颜色的变化,即如果它是红色的,则将其更改为黑色,反之亦然。必须注意的是,NULL 节点的颜色始终为黑色。此外,我们总是先尝试重新着色,如果重新着色不起作用,那么我们就进行轮换。
(2)旋转
4、红黑树的应用
1、大多数自平衡 BST 库函数,如 C++ 中的 map 和 set(或 Java 中的 TreeSet 和 TreeMap,HashMap(数组+链表/红黑树))都使用红黑树。
2、它用于实现Linux的CPU调度。Completely Fair Scheduler使用它。
3、它们还用于 K-mean 聚类算法以降低时间复杂度。
4、MySQL 还使用红黑树作为表索引。
5、Java实现遍历和插入
/*package whatever //do not write package name here */
import java.io.*;
// considering that you know what are red-black trees here is the implementation in java for insertion and traversal.
// RedBlackTree class. This class contains subclass for node
// as well as all the functionalities of RedBlackTree such as - rotations, insertion and
// inoredr traversal
public class RedBlackTree
{
public Node root;//root node
public RedBlackTree()
{
super();
root = null;
}
// node creating sublass
class Node
{
int data;
Node left;
Node right;
char colour;
Node parent;
Node(int data)
{
super();
this.data = data; // only including data. not key
this.left = null; // left subtree
this.right = null; // right subtree
this.colour = 'R'; // colour . either 'R' or 'B'
this.parent = null; // required at time of rechecking.
}
}
// this function performs left rotation
Node rotateLeft(Node node)
{
Node x = node.right;
Node y = x.left;
x.left = node;
node.right = y;
node.parent = x; // parent resetting is also important.
if(y!=null)
y.parent = node;
return(x);
}
//this function performs right rotation
Node rotateRight(Node node)
{
Node x = node.left;
Node y = x.right;
x.right = node;
node.left = y;
node.parent = x;
if(y!=null)
y.parent = node;
return(x);
}
// these are some flags.
// Respective rotations are performed during traceback.
// rotations are done if flags are true.
boolean ll = false;
boolean rr = false;
boolean lr = false;
boolean rl = false;
// helper function for insertion. Actually this function performs all tasks in single pass only.
Node insertHelp(Node root, int data)
{
// f is true when RED RED conflict is there.
boolean f=false;
//recursive calls to insert at proper position according to BST properties.
if(root==null)
return(new Node(data));
else if(data<root.data)
{
root.left = insertHelp(root.left, data);
root.left.parent = root;
if(root!=this.root)
{
if(root.colour=='R' && root.left.colour=='R')
f = true;
}
}
else
{
root.right = insertHelp(root.right,data);
root.right.parent = root;
if(root!=this.root)
{
if(root.colour=='R' && root.right.colour=='R')
f = true;
}
// at the same time of insertion, we are also assigning parent nodes
// also we are checking for RED RED conflicts
}
// now lets rotate.
if(this.ll) // for left rotate.
{
root = rotateLeft(root);
root.colour = 'B';
root.left.colour = 'R';
this.ll = false;
}
else if(this.rr) // for right rotate
{
root = rotateRight(root);
root.colour = 'B';
root.right.colour = 'R';
this.rr = false;
}
else if(this.rl) // for right and then left
{
root.right = rotateRight(root.right);
root.right.parent = root;
root = rotateLeft(root);
root.colour = 'B';
root.left.colour = 'R';
this.rl = false;
}
else if(this.lr) // for left and then right.
{
root.left = rotateLeft(root.left);
root.left.parent = root;
root = rotateRight(root);
root.colour = 'B';
root.right.colour = 'R';
this.lr = false;
}
// when rotation and recolouring is done flags are reset.
// Now lets take care of RED RED conflict
if(f)
{
if(root.parent.right == root) // to check which child is the current node of its parent
{
if(root.parent.left==null || root.parent.left.colour=='B') // case when parent's sibling is black
{// perform certaing rotation and recolouring. This will be done while backtracking. Hence setting up respective flags.
if(root.left!=null && root.left.colour=='R')
this.rl = true;
else if(root.right!=null && root.right.colour=='R')
this.ll = true;
}
else // case when parent's sibling is red
{
root.parent.left.colour = 'B';
root.colour = 'B';
if(root.parent!=this.root)
root.parent.colour = 'R';
}
}
else
{
if(root.parent.right==null || root.parent.right.colour=='B')
{
if(root.left!=null && root.left.colour=='R')
this.rr = true;
else if(root.right!=null && root.right.colour=='R')
this.lr = true;
}
else
{
root.parent.right.colour = 'B';
root.colour = 'B';
if(root.parent!=this.root)
root.parent.colour = 'R';
}
}
f = false;
}
return(root);
}
// function to insert data into tree.
public void insert(int data)
{
if(this.root==null)
{
this.root = new Node(data);
this.root.colour = 'B';
}
else
this.root = insertHelp(this.root,data);
}
// helper function to print inorder traversal
void inorderTraversalHelper(Node node)
{
if(node!=null)
{
inorderTraversalHelper(node.left);
System.out.printf("%d ", node.data);
inorderTraversalHelper(node.right);
}
}
//function to print inorder traversal
public void inorderTraversal()
{
inorderTraversalHelper(this.root);
}
// helper function to print the tree.
void printTreeHelper(Node root, int space)
{
int i;
if(root != null)
{
space = space + 10;
printTreeHelper(root.right, space);
System.out.printf("\n");
for ( i = 10; i < space; i++)
{
System.out.printf(" ");
}
System.out.printf("%d", root.data);
System.out.printf("\n");
printTreeHelper(root.left, space);
}
}
// function to print the tree.
public void printTree()
{
printTreeHelper(this.root, 0);
}
public static void main(String[] args)
{
// let us try to insert some data into tree and try to visualize the tree as well as traverse.
RedBlackTree t = new RedBlackTree();
int[] arr = {1,4,6,3,5,7,8,2,9};
for(int i=0;i<9;i++)
{
t.insert(arr[i]);
System.out.println();
t.inorderTraversal();
}
// you can check colour of any node by with its attribute node.colour
t.printTree();
}
}