紅黑樹的實現

目錄

  • 1、紅黑樹原理
    • 1、紅黑樹性質
    • 2、變換規則(從插入結點的角度來講)
      • 1.變色
      • 2.左旋
      • 3.右旋
    • 3、刪除結點需要注意的地方
  • 2、代碼
    • 1、定義結點以及構造函數
    • 2、定義紅黑樹類以及聲明它的方法
    • 3、左旋
    • 4、右旋
    • 5、插入操作
    • 6、修正操作
    • 7、刪除操作
  • 3、參考鏈接

1、紅黑樹原理

為了避免二叉搜索樹出現退化為鏈表的情況,出現了自平衡的二叉搜索樹。
AVL樹:平衡二叉樹,它的左右子樹高度之差不超過1,不過它太過于理想化。
通過性能綜合考慮選用紅黑樹。

1、紅黑樹性質

1、每個結點不是紅色就是黑色
2、不可能有連在一起的紅色結點
3、根結點都是黑色
4、每個紅色結點的兩個子結點都是黑色。
5、葉子結點都是黑色(葉子結點指的是NULL結點)
6、 從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。

2、變換規則(從插入結點的角度來講)

變換規則分為三種:

變色、左旋、右旋

1.變色

變色的前提:當前結點的父結點是紅色,且它的祖父結點的另一個結點==(叔結點)也是紅色==。
變色的過程:

1、將父結點設為黑色
2、叔結點設為黑色
3、祖父結點設為紅色
4、把指針定義到祖父結點設為當前要操作的結點。
5、繼續分析判斷

示例如下:
按照二叉搜索樹的規則插入對應的結點6;發現違反規則了,兩個紅色的連到一起了,并且符合變色的前提,所以進行變色

圖1 插入結點6,將其作為當前結點
圖2 做完變色變換后,將之前的祖父結點作為當前結點

2.左旋

觀察變色之后的樹,發現仍然是違反規則的:5、12兩個紅色連在一起了。
然后再看是不是符合變色的前提:顯然不符合,因為12的叔結點30不是紅色。那么這時就應該判斷是否符合左旋的前提了。
左旋的前提:
當前父結點是紅色,叔叔是黑色的時候,且當前的結點是右子樹。
左旋的過程:

以父結點作為中心旋轉,動態圖如下:
1、將當前以當前結點為根結點的子樹(between E and S)連接到祖父結點E
2、E和S中較大的結點作為移動到根結點
在這里插入圖片描述

左旋前后對比:

圖1 左旋前
圖2 左旋后

3.右旋

觀察左旋之后的樹,發現仍然是違反規則的:5、12兩個紅色的結點連在一起。且不符合變色和左旋的條件。
那么接下來就要使用右旋
右旋條件:
當前父結點是紅色,叔結點是黑色,且當前的結點是左子樹。
右旋的操作:

1、把父結點變為黑色
2、把祖父結點變為紅色
3、以祖父結點旋轉
在這里插入圖片描述

右旋前后對比;13重新連接。

圖1 右旋前
圖2 右旋后

3、刪除結點需要注意的地方

1、將紅黑樹當做一棵二叉搜索樹,將該結點從二叉搜索樹中刪除
2、通過旋轉和變色操作來修正這棵樹,使之重新成為一棵紅黑樹
關于二叉搜索樹的刪除結點不是這篇筆記的重點,可以參考我之前的一個刷題筆記,還是比較有用處的:

二叉搜索樹的插入、刪除、修剪、構造操作(leetcode701、450、669、108)

怕麻煩的話也可以直接看下面的截圖:
在這里插入圖片描述

2、代碼

1、定義結點以及構造函數

每個結點有顏色和值,左右孩子以及父結點指針。
定義帶參構造函數

template <class T>
class RBTNode{public:RBTColor color;    // 顏色T key;            // 關鍵字(鍵值)RBTNode *left;    // 左孩子RBTNode *right;    // 右孩子RBTNode *parent; // 父結點RBTNode(T value, RBTColor c, RBTNode *p, RBTNode *l, RBTNode *r):key(value),color(c),parent(),left(l),right(r) {}
};

2、定義紅黑樹類以及聲明它的方法

每棵樹有一個根結點還有空結點

template <class T>
class RBTree {private:RBTNode<T> *mRoot;    // 根結點public:RBTree();~RBTree();// 前序遍歷"紅黑樹"void preOrder();// 中序遍歷"紅黑樹"void inOrder();// 后序遍歷"紅黑樹"void postOrder();// (遞歸實現)查找"紅黑樹"中鍵值為key的節點RBTNode<T>* search(T key);// (非遞歸實現)查找"紅黑樹"中鍵值為key的節點RBTNode<T>* iterativeSearch(T key);// 查找最小結點:返回最小結點的鍵值。T minimum();// 查找最大結點:返回最大結點的鍵值。T maximum();// 找結點(x)的后繼結點。即,查找"紅黑樹中數據值大于該結點"的"最小結點"。RBTNode<T>* successor(RBTNode<T> *x);// 找結點(x)的前驅結點。即,查找"紅黑樹中數據值小于該結點"的"最大結點"。RBTNode<T>* predecessor(RBTNode<T> *x);// 將結點(key為節點鍵值)插入到紅黑樹中void insert(T key);// 刪除結點(key為節點鍵值)void remove(T key);// 銷毀紅黑樹void destroy();// 打印紅黑樹void print();private:// 前序遍歷"紅黑樹"void preOrder(RBTNode<T>* tree) const;// 中序遍歷"紅黑樹"void inOrder(RBTNode<T>* tree) const;// 后序遍歷"紅黑樹"void postOrder(RBTNode<T>* tree) const;// (遞歸實現)查找"紅黑樹x"中鍵值為key的節點RBTNode<T>* search(RBTNode<T>* x, T key) const;// (非遞歸實現)查找"紅黑樹x"中鍵值為key的節點RBTNode<T>* iterativeSearch(RBTNode<T>* x, T key) const;// 查找最小結點:返回tree為根結點的紅黑樹的最小結點。RBTNode<T>* minimum(RBTNode<T>* tree);// 查找最大結點:返回tree為根結點的紅黑樹的最大結點。RBTNode<T>* maximum(RBTNode<T>* tree);// 左旋void leftRotate(RBTNode<T>* &root, RBTNode<T>* x);// 右旋void rightRotate(RBTNode<T>* &root, RBTNode<T>* y);// 插入函數void insert(RBTNode<T>* &root, RBTNode<T>* node);// 插入修正函數void insertFixUp(RBTNode<T>* &root, RBTNode<T>* node);// 刪除函數void remove(RBTNode<T>* &root, RBTNode<T> *node);// 刪除修正函數void removeFixUp(RBTNode<T>* &root, RBTNode<T> *node, RBTNode<T> *parent);// 銷毀紅黑樹void destroy(RBTNode<T>* &tree);// 打印紅黑樹void print(RBTNode<T>* tree, T key, int direction);#define rb_parent(r)   ((r)->parent)
#define rb_color(r) ((r)->color)
#define rb_is_red(r)   ((r)->color==RED)
#define rb_is_black(r)  ((r)->color==BLACK)
#define rb_set_black(r)  do { (r)->color = BLACK; } while (0)
#define rb_set_red(r)  do { (r)->color = RED; } while (0)
#define rb_set_parent(r,p)  do { (r)->parent = (p); } while (0)
#define rb_set_color(r,c)  do { (r)->color = (c); } while (0)
};

3、左旋

/* * 對紅黑樹的節點(x)進行左旋轉** 左旋示意圖(對節點x進行左旋):*      px                              px*     /                               /*    x                               y                *   /  \      --(左旋)-->           / \                #*  lx   y                          x  ry     *     /   \                       /  \*    ly   ry                     lx  ly  ***/
template <class T>
void RBTree<T>::leftRotate(RBTNode<T>* &root, RBTNode<T>* x)
{// 設置x的右孩子為yRBTNode<T> *y = x->right;// 將 “y的左孩子” 設為 “x的右孩子”;// 如果y的左孩子非空,將 “x” 設為 “y的左孩子的父親”x->right = y->left;if (y->left != NULL)y->left->parent = x;// 將 “x的父親” 設為 “y的父親”y->parent = x->parent;if (x->parent == NULL){root = y;            // 如果 “x的父親” 是空節點,則將y設為根節點}else{if (x->parent->left == x)x->parent->left = y;    // 如果 x是它父節點的左孩子,則將y設為“x的父節點的左孩子”elsex->parent->right = y;    // 如果 x是它父節點的左孩子,則將y設為“x的父節點的左孩子”}// 將 “x” 設為 “y的左孩子”y->left = x;// 將 “x的父節點” 設為 “y”x->parent = y;
}

4、右旋

/* * 對紅黑樹的節點(y)進行右旋轉** 右旋示意圖(對節點y進行左旋):*            py                               py*           /                                /*          y                                x                  *         /  \      --(右旋)-->            /  \                     #*        x   ry                           lx   y  *       / \                                   / \                   #*      lx  rx                                rx  ry* */
template <class T>
void RBTree<T>::rightRotate(RBTNode<T>* &root, RBTNode<T>* y)
{// 設置x是當前節點的左孩子。RBTNode<T> *x = y->left;// 將 “x的右孩子” 設為 “y的左孩子”;// 如果"x的右孩子"不為空的話,將 “y” 設為 “x的右孩子的父親”y->left = x->right;if (x->right != NULL)x->right->parent = y;// 將 “y的父親” 設為 “x的父親”x->parent = y->parent;if (y->parent == NULL) {root = x;            // 如果 “y的父親” 是空節點,則將x設為根節點}else{if (y == y->parent->right)y->parent->right = x;    // 如果 y是它父節點的右孩子,則將x設為“y的父節點的右孩子”elsey->parent->left = x;    // (y是它父節點的左孩子) 將x設為“x的父節點的左孩子”}// 將 “y” 設為 “x的右孩子”x->right = y;// 將 “y的父節點” 設為 “x”y->parent = x;
}

5、插入操作

內部接口 – insert(root, node)的作用是將"node"節點插入到紅黑樹中。其中,root是根,node是被插入節點。
外部接口 – insert(key)的作用是將"key"添加到紅黑樹中。

/* * 將結點插入到紅黑樹中** 參數說明:*     root 紅黑樹的根結點*     node 插入的結點        // 對應《算法導論》中的node*/
template <class T>
void RBTree<T>::insert(RBTNode<T>* &root, RBTNode<T>* node)
{RBTNode<T> *y = NULL;RBTNode<T> *x = root;// 1. 將紅黑樹當作一顆二叉查找樹,將節點添加到二叉查找樹中。while (x != NULL){y = x;if (node->key < x->key)x = x->left;elsex = x->right;}node->parent = y;if (y!=NULL){if (node->key < y->key)y->left = node;elsey->right = node;}elseroot = node;// 2. 設置節點的顏色為紅色node->color = RED;// 3. 將它重新修正為一顆二叉查找樹insertFixUp(root, node);
}/* * 將結點(key為節點鍵值)插入到紅黑樹中** 參數說明:*     tree 紅黑樹的根結點*     key 插入結點的鍵值*/
template <class T>
void RBTree<T>::insert(T key)
{RBTNode<T> *z=NULL;// 如果新建結點失敗,則返回。if ((z=new RBTNode<T>(key,BLACK,NULL,NULL,NULL)) == NULL)return ;insert(mRoot, z);
}

6、修正操作

根據條件,分別進行變色、左旋、右旋操作,最后再將根結點置為黑色。
對于叔叔結點是組父結點的左孩子還是右孩子需要分類討論。
操作的具體步驟,可以返回上文對比看。
insertFixUp(root, node)的作用是對應"上面所講的第三步"。它是一個內部接口。

/** 紅黑樹插入修正函數** 在向紅黑樹中插入節點之后(失去平衡),再調用該函數;* 目的是將它重新塑造成一顆紅黑樹。** 參數說明:*     root 紅黑樹的根*     node 插入的結點        // 對應《算法導論》中的z*/
template <class T>
void RBTree<T>::insertFixUp(RBTNode<T>* &root, RBTNode<T>* node)
{RBTNode<T> *parent, *gparent;// 若“父節點存在,并且父節點的顏色是紅色”while ((parent = rb_parent(node)) && rb_is_red(parent)){gparent = rb_parent(parent);//若“父節點”是“祖父節點的左孩子”if (parent == gparent->left){RBTNode<T> *uncle = gparent->right;// Case 1條件:叔叔節點是紅色,此時采用變色if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}// Case 2條件:叔叔是黑色,且當前節點是右孩子,此時采用左旋if (parent->right == node){RBTNode<T> *tmp;leftRotate(root, parent);tmp = parent;parent = node;node = tmp;}// Case 3條件:叔叔是黑色,且當前節點是左孩子,此時采用右旋rb_set_black(parent);rb_set_red(gparent);rightRotate(root, gparent);} else//若“z的父節點”是“z的祖父節點的右孩子”{RBTNode<T> *uncle = gparent->left;// Case 1條件:叔叔節點是紅色if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}// Case 2條件:叔叔是黑色,且當前節點是左孩子if (parent->left == node){RBTNode<T> *tmp;rightRotate(root, parent);tmp = parent;parent = node;node = tmp;}// Case 3條件:叔叔是黑色,且當前節點是右孩子。rb_set_black(parent);rb_set_red(gparent);leftRotate(root, gparent);}}// 將根節點設為黑色rb_set_black(root);
}

7、刪除操作

將紅黑樹內的某一個節點刪除。需要執行的操作依次是:首先,將紅黑樹當作一顆二叉查找樹,將該節點從二叉查找樹中刪除;然后,通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。

3、參考鏈接

1、紅黑樹 - C++代碼實現
2、leetcode刷題(十):樹(紅黑樹,B樹)
3、B站視頻,關于紅黑樹的推導
4、慕課視頻,關于模板類的寫法指導
5、二叉搜索樹的插入、刪除、修剪、構造操作(leetcode701、450、669、108)
6、紅黑樹的刪除調整過程(轉載)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/377301.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/377301.shtml
英文地址,請注明出處:http://en.pswp.cn/news/377301.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

118 - ZOJ Monthly, July 2012

http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId339 都是賽后做的。。。弱爆了 A題是找由2和5組成的數字的個數 直接打個表就行了 只是比賽的時候不知道怎么打表啊。。 View Code #include<cstdio> #include<cstring> #include<algorith…

edp1.2和edp1.4_EDP??的完整形式是什么?

edp1.2和edp1.4EDP??&#xff1a;電子數據處理 (EDP: Electronic Data Processing) EDP is an abbreviation of Electronic Data Processing. It alludes to the functioning of operations of commercial data, documents processing of storing, with the use of a compute…

高效讀書心得

1.盡量閱讀中文版 雖然有人英文很強&#xff0c;有的翻譯很差&#xff0c;但AnyWay 中文閱讀與理解的時間&#xff0c;略讀與快速定位感興趣內容的速度還是要快一些。 2.即時批注、總結筆記與交流 雖然愛書&#xff0c;但發現最有效的讀書方式還是不斷的制造脂批本&…

《MySQL——增刪改查以及常用語法》

目錄登錄和退出MySQL服務器基本語法&#xff08;增刪改查&#xff09;登錄和退出MySQL服務器 # 登錄MySQL 密碼 $ mysql -u root -p12345612 # 退出MySQL數據庫服務器 exit;基本語法&#xff08;增刪改查&#xff09; -- 顯示所有數據庫 show databases;-- 創建數據庫 CREA…

WCF簡介

一、簡介 WCF是Windows Communication Foundation縮寫&#xff0c;是Microsoft為構建面向服務的應用提供的分布式通信編程框架&#xff0c;是.NET Framework 3.5的重要組成部分。使用該框架&#xff0c;開發人員可以構建跨平臺、安全、可靠和支持事務處理的企業級互聯應用解決方…

css鏈接樣式_CSS中的樣式鏈接

css鏈接樣式CSS樣式鏈接 (CSS Styling Links) The links in CSS can be styled in various ways to make our website more presentable and attractive. The links can also be styled depending on their states e.g. visited, active, hover, etc. CSS中的鏈接可以通過各種方…

《MySQL——約束》

目錄主鍵約束唯一主鍵非空約束默認約束外鍵約束主鍵約束 -- 主鍵約束 -- 使某個字段不重復且不得為空&#xff0c;確保表內所有數據的唯一性。 CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(20) );-- 聯合主鍵 -- 聯合主鍵中的每個字段都不能為空&#xff0c;并且加起…

UIControl事件

CHENYILONG BlogUIControl事件 FullscreenUIControl事件1.UIControlEventTouchDown單點觸摸按下事件&#xff1a;用戶點觸屏幕&#xff0c;或者又有新手指落下的時候。2.UIControlEventTouchDownRepeat多點觸摸按下事件&#xff0c;點觸計數大于1&#xff1a;用戶按下第二、三、…

C++ 為什么要使用#ifdef __cplusplus extern C { #endif

經常看到別人的頭文件 有這樣的代碼 #ifdef __cplusplus extern "C" { #endif// C 樣式 的函數#ifdef __cplusplus } #endif 為什么要這樣呢&#xff1f; 因為 C 語言不支持重載函數 也就是同名函數&#xff0c;參數卻不一樣,C支持&#xff0c;其編譯器對函數名的處理…

css中的媒體查詢_CSS中的媒體查詢

css中的媒體查詢CSS | 媒體查詢 (CSS | Media Queries) Creating a web page is not an easy task as it requires loads of content and data so that it becomes strongly responsive to the users. To do that various contents are even added e.g.: resources, informativ…

SharePoint2013安裝組件時AppFabric時出現1603錯誤,解決方法:

采用PowerShell命令批量下載必備組件: 下載完成后&#xff0c;采用批處理命令安裝必備組件。 注&#xff1a;SPS2013安裝必備組件及批處理下載地址&#xff1a; 需要將必備組件放在安裝文件的PrerequisiteInstallerFiles文件夾中&#xff0c;將PreReq2013.bat放在安裝文件根目錄…

《MySQL——數據表設計三大范式》

目錄數據表設計范式第一范式第二范式第三范式數據表設計范式 第一范式 數據表中的所有字段都是不可分割的原子值。 字段值還可以繼續拆分的&#xff0c;就不滿足第一范式&#xff0c;如下&#xff1a; 下面這個&#xff0c;更加貼合第一范式&#xff1a; 范式設計得越詳細&…

三道簡單樹型dp+01背包~~hdu1561,poj1947,zoj3626

以前學樹型dp就是隨便的看了幾道題&#xff0c;沒有特別注意樹型dp中的小分類的總結&#xff0c;直到上次浙大月賽一道很簡單的樹型dp都不會&#xff0c;才意識到自己太水了&#xff5e;&#xff5e;come on&#xff01; hdu1561&#xff1a;題目給出了很多棵有根樹&#xff0c…

css 字體圖標更改顏色_在CSS中更改字體

css 字體圖標更改顏色CSS字體屬性 (CSS font properties ) Font properties in CSS is used to define the font family, boldness, size, and the style of a text. CSS中的字體屬性用于定義字體系列 &#xff0c; 粗體 &#xff0c; 大小和文本樣式 。 Syntax: 句法&#xf…

深入new/delete:Operator new的全局重載

Operator new 的全局重載 原文地址&#xff1a;http://blog.csdn.net/zhenjing/article/details/4354880 我們經常看到這么一句話&#xff1a; operator new 可以重載&#xff0c; placement new 不可重載。其實此處所說的不可重載應該是指全局的 placement new 不可重載&#…

C++基礎知識點整理

基本語法 1、static關鍵字的作用 1、全局靜態變量 加了static關鍵字的全局變量只能在本文件中使用。 存儲在靜態存儲區&#xff0c;整個程序運行期間都存在。 2、局部靜態變量 作用域仍為局部作用域。 不過離開作用域之后&#xff0c;并沒有銷毀&#xff0c;而是貯存程序中&a…

Haskell學習筆記

《learn you a Haskell》這書的結構與常見的語言入門教材完全不一樣。事實上&#xff0c;即使學到第八章&#xff0c;你還是寫不出正常的程序…因為到現在為止還沒告訴你入口點模塊怎么寫&#xff0c;IO部分也留在了最后幾章才介紹。最重要的是&#xff0c;沒有系統的總結數據類…

組合問題 已知組合數_組合和問題

組合問題 已知組合數Description: 描述&#xff1a; This is a standard interview problem to make some combination of the numbers whose sum equals to a given number using backtracking. 這是一個標準的面試問題&#xff0c;它使用回溯功能將總和等于給定數字的數字進…

可變參數模板、右值引用帶來的移動語義完美轉發、lambda表達式的理解

可變參數模板 可變參數模板對參數進行了高度泛化&#xff0c;可以表示任意數目、任意類型的參數&#xff1a; 語法為&#xff1a;在class或者typename后面帶上省略號。 Template<class ... T> void func(T ... args) {// }T:模板參數包&#xff0c;args叫做函數參數包 …

BI-SqlServer

一.概述 SqlServer數據倉庫ETL組件 IntegrationServiceOLAP組件 AnalysisService報表 ReportingServiceMDX(查多維數據集用的)和DMX(查挖掘模型用的)。二.商業智能-Analysis Services 項目 構建挖掘模型1構建挖掘模型2構建挖掘模型3三.商業智能-SqlServerAnalysis-Asp.net WebS…