0
点赞
收藏
分享

微信扫一扫

数据结构和算法 十一、红黑树

一、红黑树(Red-Black Tree)

1、概述

        红黑树是一种平衡二叉搜索树,其中每个节点都包含一个额外的信息位,表示节点的颜色。节点的颜色可以是红色或黑色,这取决于节点存储的位信息。

        这些颜色用于确保树在插入和删除期间保持平衡。虽然树的平衡并不完美,但减少搜索时间并将其保持在 O(log n) 时间左右就足够了,其中 n 是树中元素的总数。红黑树树是鲁道夫拜耳于 1972 年发明的。

        每棵红黑树都遵循的规则: 

        1、每个节点都有红色或黑色的颜色。

        2、树的根总是黑色的。

        3、没有两个相邻的红色节点(红色节点不能有红色父节点或红色子节点)。

        4、从节点(包括根节点)到其任何后代 NULL 节点的每条路径都具有相同数量的黑色节点。

        5、所有叶子节点都是黑色节点。

2、和AVL树相比

        与红黑树相比,AVL 树更平衡,但它们在插入和删除过程中可能会导致更多的旋转。因此,如果您的应用程序涉及频繁的插入和删除,那么应该首选红黑树。如果插入和删除不那么频繁,而搜索是一种更频繁的操作,那么 AVL 树应该优先于红黑树。

3、红黑树如何平衡

(1)重新着色

        重新着色是节点颜色的变化,即如果它是红色的,则将其更改为黑色,反之亦然。必须注意的是,NULL 节点的颜色始终为黑色。此外,我们总是先尝试重新着色,如果重新着色不起作用,那么我们就进行轮换。

(2)旋转

原始树

插入节点4,已经不是一颗红黑树

转变5、7、8的颜色

进行移动、旋转,之后仍然不是红黑树

 

再次重新着色,最后是一颗红黑树了

 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();
	}
}

举报

相关推荐

0 条评论