1. 程式人生 > >【資料結構】紅黑樹(如何實現及怎樣判斷)

【資料結構】紅黑樹(如何實現及怎樣判斷)

      紅黑樹是一顆二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是red或black。通過對任何一條從根節點到葉子節點的簡單路徑上的顏色來約束,紅黑樹保證了最長路徑不超過最短路經的兩倍,因此近似於平衡

紅黑樹的規則:

1、每個節點不是紅色就是黑色的。

2、根結點是黑色的。

3、如果一個節點是紅色的,則它的兩個子結點是黑色的。即每條路徑上不能存在兩個連續的紅節點。

4、對每個節點,從該節點到其他節點的簡單路徑上,均包含相同數目的黑色節點

紅黑樹節點RBTreeNode的實現,利用三叉鏈(left、right、parent)、key、value及顏色col。

enum colour
{
	RED,
	BLACK,
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	K _key;
	V _value;
	colour _col;
	RBTreeNode(const K& key = K(), const V& value = V())
		:_left(NULL)
		, _right(NULL)
		, _parent(NULL)
		, _key(key)
		, _value(value)
		, _col(RED)//初始化插入節點的顏色為紅色,不影響黑節點個數
	{}
};

一、紅黑樹的插入

      紅黑樹的插入類似於二叉搜尋樹,但是每次插入後都到要注意是否滿足紅黑樹的規則,特別是規則3和規則4。如果不滿足就需要調整樹的結構,下面對插入節點時分成以下幾種情況:

注:cur(插入節點)parent(cur的父親節點)grandfather(parent的父親節點)uncle(父親的兄弟節點)

1、根節點root為空,直接插入新節點並給root,設定根節點的顏色為BLACK。

2、根節點root不為空,找到插入節點的位置並插入節點cur。cur節點是紅色,若parent是紅節點,則需要進行調整。

      存在以下三種情況:

      情況一:cur為紅,parent為紅,grandfather為黑,uncle存在且為紅。

     調整方案,如下如所示:

   

      情況二:cur為紅,parent為紅,grandfather為黑,uncle不存在或者uncle為黑。

                     左單旋:parent為grantfather的右孩子,cur為parent的右孩子。

                     右單旋: parent為grantfather的左孩子,cur為parent的左孩子。

      調整方案,下面以右單旋進行分析,如下圖所示:

     

       左單旋轉類似右單旋轉的實現過程。

      情況三:cur為紅,parent為紅,grantfather為黑,uncle不存在或者uncle為黑。

                    左右單旋:parent為grantfather的右孩子,cur為parent的左孩子。

                    右左單旋:parent為grantfather的左孩子,cur為parent的右孩子。

      調整方案,下面以做右單旋進行分析,如下圖所示:

    

      右左旋轉的實現類似左單旋轉的實現過程。

具體實現如下:

RBTree()
 :_root(NULL)
{}
bool Insert(const K& key, const V& value)
{
 //1、_root為空時,插入節點是根節點
 if (_root == NULL)
 {
  _root = new Node(key, value);
  _root->_col = BLACK;
  return true;
 }
 Node* parent = NULL;
 Node* cur = _root;
 while (cur)//找到插入節點的位置
 {
  if (cur->_key > key)
  {
   parent = cur;
   cur = cur->_left;
  }
  else if (cur->_key < key)
  {
   parent = cur;
   cur = cur->_right;
  }
  else
   return false;
 }
 cur = new Node(key, value);//插入節點
 if (parent->_key > cur->_key)
 {
  parent->_left = cur;
  cur->_parent = parent;
 }
 if (parent->_key < cur->_key)
 {
  parent->_right = cur;
  cur->_parent = parent;
 }
 //2、出現兩個連續紅節點,進行調整
 while (cur != _root && parent->_col == RED)//此條件說明存在parent節點,parent存在父親節點
 {
  Node* grandfather = parent->_parent;
  if (parent == grandfather->_left)
  {
   Node* uncle = grandfather->_right;
   if (uncle && uncle->_col == RED)//情況一:uncle為RED
   {
    grandfather->_col = RED;
    parent->_col = BLACK;
    uncle->_col = BLACK;
   }
   else //情況二、三:uncle為BLACK或不存在(右單旋或左右單旋)
   {//先考慮左右單旋,先進行左單旋,轉化為情況二,再進行右單旋
    if (cur == parent->_right)
    {
     _RotateL(parent);//左旋不需要變顏色
    }
    _RotateR(grandfather);
   }
  }
  else
  {
   Node* grandfather = parent->_parent;
   if (parent == grandfather->_right)
   {
    Node* uncle = grandfather->_left;
    if (uncle && uncle->_col == RED)//情況一:uncle為RED
    {
     grandfather->_col = RED;
     parent->_col = BLACK;
     uncle->_col = BLACK;
    }
    else //情況二、三:uncle為BLACK或不存在(左單旋或右左單旋)
    {
     if (cur == parent->_left)
     {
      _RotateR(parent);//右旋不需要變顏色
     }
     _RotateL(grandfather);
    }
   }
  }
  cur = grandfather;
  parent = cur->_parent;
  _root->_col = BLACK;
 }
}
void _RotateL(Node* parent)
{
 Node* SubR = parent->_right;
 Node* SubRL = SubR->_left;
 parent->_right = SubRL;
 if (SubRL)
  SubRL->_parent = parent;
 SubR->_parent = parent->_parent;
 SubR->_left = parent;
 parent->_parent = SubR;
 //變色
 SubR->_col = BLACK;//情況二中:parent變黑,grandfather變紅
 parent->_col = RED;
 parent = SubR;
 if (parent->_parent == NULL)
  _root = parent;
 else
 {
  Node* ppNode = parent->_parent;
  if (ppNode->_key > parent->_key)
   ppNode->_left = parent;
  else
   ppNode->_right = parent;
 }
}
void _RotateR(Node* parent)
{
 Node* SubL = parent->_left;
 Node* SubLR = SubL->_right;
 parent->_left = SubLR;
 if (SubLR)
  SubLR->_parent = parent;
 SubL->_parent = parent->_parent;
 SubL->_right = parent;
 parent->_parent = SubL;
 //變色
 SubL->_col = BLACK;//情況二中:parent變黑,grandfather變紅
 parent->_col = RED;
 parent = SubL;
 if (parent->_parent == NULL)
  _root = parent;
 else
 {
  Node* ppNode = parent->_parent;
  if (ppNode->_key > parent->_key)
   ppNode->_left = parent;
  else
   ppNode->_right = parent;
 }
}

二、紅黑樹的判斷

1、根結點是否滿足紅黑樹規則,是否為黑色。

2、每條路徑的黑色節點相等。統計出一條路徑的黑色節點的個數,然後與其他路徑黑色節點個數進行比較。

3、不存在連續的紅色節點,判斷紅色節點的父親節點是否為紅色。

具體實現如下:

bool Check()
{
	if (_root->_col == RED)
		return false;
	int count = 0;//統計出一條路徑的黑色節點的個數
	int num = 0;//需要與count比較的其他路徑黑色節點個數
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
			count++;
		cur = cur->_left;
	}
	return _Check(_root, count, num);
}
bool _Check(Node* root, int BlackNum, int CurBlackNum)
{
 if (root == NULL)
  return true;
 if (root->_col == RED && root->_parent->_col == RED)//存在兩個連續的紅節點
  return false;
 if (root->_col == BLACK)//黑節點就CurBlackNum++
  CurBlackNum++;
 if (root->_left == NULL && root->_right == NULL)
 {
  if (CurBlackNum == BlackNum)
   return true;
  else//黑色節點不相等返回false
   return false;
 }
 return _Check(root->_left, BlackNum, CurBlackNum) 
  && _Check(root->_right, BlackNum, CurBlackNum);//進行左右遞迴
}

紅黑樹的效率:

1、最壞情況下,紅黑樹高度不超過2lgN

最壞的情況就是,紅黑樹中除了最左側路徑全部是由3-node節點組成,即紅黑相間的路徑長度是全黑路徑長度的2倍

2、紅黑樹的平均高度大約為lgN

紅黑樹的運用(高效的二叉搜尋樹)

紅黑樹這種資料結構應用十分廣泛,在多種程式語言中被用作符號表的實現,如:

  • Java中的java.util.TreeMap,java.util.TreeSet
  • C++ STL中的:map,multimap,multiset

紅黑樹和AVL樹的比較

1、紅黑樹和AVL樹都是高效的平衡二叉樹,增刪查改的時間複雜度都是O(lg(N))

2、紅黑樹的不追求完全平衡,保證最長路徑不超過最短路徑的2倍,相對而言,降低了旋轉的要求,所以效能優於AVL樹,所以實際運用中紅黑樹更多。紅黑樹是一種特殊的二叉查詢樹,他的查詢方法也和二叉查詢樹一樣,不需要做太多更改,但是由於紅黑樹比一般的二叉查詢樹具有更好的平衡,所以查詢起來更快。

以上為個人學習的一些總結,如有紕漏,請多多指教。



相關推薦

資料結構如何實現怎樣判斷

      紅黑樹是一顆二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是red或black。通過對任何一條從根節點到葉子節點的簡單路徑上的顏色來約束,紅黑樹保證了最長路徑不超過最短路經的兩倍,因此近似於平衡。 紅黑樹的規則: 1、每個節點不是紅色就是

資料結構

一、概念 Red-Black Tree 簡稱 R-B Tree,是一種自平衡二叉查詢樹,是在電腦科學中用到的一種資料結構,典型的用途是實現關聯陣列。 二、特性 (1)每個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每個葉子節點(NIL)是黑

資料結構的插入(Insert)

前言:    紅黑樹是一棵二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是Red或Black。通過對任何一條從根到葉子簡單路徑上的顏色來約束,紅黑樹保證最長路徑不超過最短路徑的兩倍,因而近似於平衡。 紅黑樹的基本概念:   紅黑樹是滿足下面紅黑性質的二叉

資料結構-

1.紅-黑樹的特徵         它主要有兩個特徵:1.節點都有顏色;2.在插入和刪除的過程中,要遵循保持這些顏色的不同排列的規則。首先第一個特徵很好解決,在節點類中店家一個數據欄位,例如boolean型變數,以此來表示節點的顏色資訊。第二個特徵比較複雜,紅-黑樹有它

資料結構二叉的構建遍歷遞迴演算法

題目描述: 編一個程式,讀入使用者輸入的一串先序遍歷字串,根據此字串建立一個二叉樹(以指標方式儲存)。 例如如下的先序遍歷字串: ABC##DE#G##F### 其中“#”表示的是空格,空格字元代表空樹。建立起此二叉樹以後,再對二叉樹進行中序遍歷,輸出遍歷結果。 具體程式

linux核心分析--核心中的資料結構

#include<linux/rbtree.h> #include <linux/string.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); struct student { int id;

linux核心分析--核心中的資料結構

紅黑樹由於節點顏色的特性,保證其是一種自平衡的二叉搜尋樹。 紅黑樹的一系列規則雖然實現起來比較複雜,但是遵循起來卻比較簡單,而且紅黑樹的插入,刪除效能也還不錯。 所以紅黑樹在核心中的應用非常廣泛,掌握好紅黑樹,即有利於閱讀核心原始碼,也可以在自己的程式碼中借鑑這種資料結構。 紅黑樹必

資料結構——插入操作

插入或刪除操作,都有可能改變紅黑樹的平衡性,利用顏色變化與旋轉這兩大法寶就可應對所有情況,將不平衡的紅黑樹變為平衡的紅黑樹。 在進行顏色變化或旋轉的時候,往往要涉及祖孫三代節點:X表示操作的基準節點,P代表X的父節點,G代表X的父節點的父節點。 我們先來大體預覽一下插入的

資料結構二叉順序儲存、鏈式儲存的JAVA程式碼實現

二叉樹是一種非線性的資料結構。它是由n個有限元素的集合,該集合或者為空、或者由一個稱為根(root)的元素及兩顆不相交的、被分別稱為左子樹、右子樹的二叉樹組成。當集合為空時,稱該二叉樹為空二叉樹。在二叉樹中,一個元素也可以稱做一個結點。二叉樹是有序的,即若將其左右兩個子樹顛倒

資料結構二叉實現

上篇部落格中,我們詳細說明了樹和二叉樹的資料結構及其特徵,本次,我們用C++來實現一下二叉樹 定義二叉樹節點結構 二叉樹需要定義指向左孩子和右孩子節點的指標,還有儲存的資料;我們在這把它的建構函式也寫出來 //定義一個二叉樹節點 template<typename

資料結構圖的構建鄰接表法

#include<iostream> #include<string> #include<queue> using namespace std; #define ERROR 1 #define MAX_VERTEX_NUM 100 typedef struct ArcNod

資料結構資料結構探索—— R-B Tree

紅黑樹,一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。 紅黑樹有五個性質:     性質1. 節點是紅色或黑色。     性質2. 根節點是黑色。     性質3 每個葉節點(NIL節點,空節點)是黑色的。     性質4 每

資料結構二叉的相關操作待更

#include "stdio.h" #include "stdlib.h" typedef struct node { char data; struct node *rchild,*lchild; }bintnode; typedef bintnode *bintree;//指向該結構體

資料結構二叉的建立和遍歷非遞迴

該程式使用的是遞迴地建立方法,以及非遞迴的遍歷演算法 執行環境:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild

資料結構二叉的建立與遍歷遞迴

該程式全是使用遞迴的操作 執行環境是:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild,*rchild; }bi

資料結構學習筆記------附c++程式碼

1、紅黑樹簡介 紅黑樹是二叉查詢樹的一種,其增刪改查的統計效能要優於AVL樹,查詢、插入、刪除演算法的複雜度都為O(log(n))。先附上紅黑樹這種資料結構的性質: 性質1、節點是紅色或黑色。 性質2、根節點是黑色。 性質3、每個葉節點(是指的空節點,nil節點)是黑色的。 性質4、

演算法二叉概念與查詢

誒,演算法這個東西,其實沒那麼簡單,但是也沒那麼難。 紅黑樹,其實已經有很多大佬都整理過了,而且文章部落格都寫得超好,我寫這篇文章的目的是:自己整理一次,這些知識才是自己的,否則永遠是別人的~   該系列到現在暫只有3篇文章: 【演算法】紅黑樹(二叉樹)概念與查詢(一):h

資料結構與演算法:Red Black Tree

一、簡介 紅黑樹(Red Black Tree)是一棵二叉查詢樹,在每個節點增加一個屬性表示節點顏色,值為紅色(Red)或者黑色(Black)。紅黑樹也是“平衡”樹中的一種,通過對任何一條從根到葉子的路徑上各個節點的顏色來進行約束,確保沒有一條路徑會比其他

資料結構——red-black tree

RB-tree(紅黑樹)是一種平衡二叉搜尋樹,它每個節點上增加了一個儲存位來表示節點的顏色,可以是 Red 或 Black,故得名。通過對任何一條從根到葉子的簡單路徑上各個節點的顏色進行約束,紅黑樹能夠確保沒有一條路徑會比其他路徑長出 2 倍,近似於平衡。

資料結構---------二叉面試題具體的所有實現

實現二叉樹的相關的操作: 先序遍歷樹(遞迴) 中序遍歷樹(遞迴) 後序遍歷樹(遞迴) 層序遍歷樹 建立一棵樹 樹的銷燬 樹的拷貝 二叉樹中節點的個數 二叉樹葉子節點的個數 二叉樹第K層節點的個數 樹的高度 在二叉樹中查詢節點 找當前節點的左子樹