紅黑樹 插入 C語言實現
紅黑樹 C語言實現
推薦一個紅黑樹視覺化網站: https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
1. 什麼是紅黑樹
https://baike.baidu.com/item/紅黑樹/2413209?fr=aladdin
紅黑樹是一種自平衡二叉查詢樹,巴拉巴拉--- , 請注意前提,是一顆二叉查詢樹。
關於二叉查詢樹: https://baike.baidu.com/item/二叉排序樹/10905079?fromtitle=二叉查詢樹&fromid=7077965&fr=aladdin
2. 紅黑樹概念
- 每個節點要麼是紅色,要麼是黑色
- 根節點和葉節點是黑色的(紅黑樹的葉節點是NULL LEAF
- 如果一個節點是紅色,那麼它的孩子是黑色的(不允許連續2個紅節點,但是允許2個連續的黑節點)
- 任意節點到葉節點的樹鏈中包含相同數量的黑節點
紅黑樹圖示
3. 紅黑樹插入
3.0 插入節點名稱規定
G: 爺爺節點
P: 父親節點
U: 叔叔節點
N: 新插入節點
3.1 插入規則
- 1 . 對於新插入的節點,顏色初始化為紅節點。
- 2 . 根節點為黑色。
- 3 . 如果在黑節點後插入,則無需進行修復。
- 4 . 如果在紅節點後插入,需要進行修復。
3.2 針對上面插入規則第三點 修復規則
-
1 . 當前節點為根節點,且為紅色,需要染成黑色。
-
2 . 若插入節點父節點為紅色,叔叔節點為紅色,則需要進行染色操作。
-
3 . 若插入節點父節點為紅色,叔叔節點為黑色,則需要進行旋轉。
如何判斷需要染色修復
插入節點的父節點為紅色,叔叔節點為紅色,則需要進行染色操作。
染色修復
- 將父親節點 和 叔叔節點 染成黑色。
- 將爺爺節點染成紅色。
- 將爺爺節點賦值給當前節點 ,再次獲取父親/叔叔/爺爺節點進行判斷。
如何判斷需要旋轉修復
插入節點的父節點為紅色,叔叔節點為黑色,則需要進行旋轉修復
旋轉修復
左左修復/左右修復/右左修復/右右修復
3.3 插入圖示
3.3.1 在黑節點後插入,則無需進行修復。
對於如下資料,我們插入一個節點 130
插入節點後如下,因為 130 的父節點為 黑節點,所以 無需進行修復
3.3.2 在紅節點後插入,需要進行修復
對於如下資料,我們插入一個150
插入之後資料如下
此時,P節點和N節點 都為紅節點,則需要進行修復
3.3.3 染色修復
父親節點 和 叔叔節點 都為紅節點 需要進行染色修復
如上圖,染色修復流程為:
- 將父親節點 和 叔叔節點 染成黑色。
- 將爺爺節點染成紅色。
- 將爺爺節點賦值給當前節點 ,再次獲取父親/叔叔/爺爺節點進行判斷。
應該修復為:
且因N為根節點,應該染成黑色,故
3.3.4 旋轉修復
1. 左左修復
如下要插入90節點
插入後如下
NULL: 空節點
修復過程:
G樹向右旋轉
然後染色,N 和 G 染為紅色,P 染為黑色
2. 左右修復
如下樹,要插入85節點
插入後如下
修復過程
P樹先向右選擇
旋轉後如下,就和【左左型別】一致了
3. 右右修復
樹如如下
要插入一個260的節點
修復過程:
G樹向左旋轉
旋轉之後
將G染成紅色,N染成紅色,P染成黑色
4. 右左修復
如下一棵樹
需要插入 248 節點,插入後如下
以P向左旋轉
旋轉後如下
和【右右型別】型別
程式碼示例
結構體定義
# define RED 1
# define BLACK 0
typedef struct {
int data; // 資料節點
struct TreeNode *left; // 左子樹
struct TreeNode *right; // 右字樹
int color; // 二叉樹顏色,1: 紅色 0:黑色
} TreeNode , *PTreeNode;
平衡二叉樹插入程式碼
PTreeNode Insert(PTreeNode *root,int data) {
printf("插入資料: %d\n",data);
TreeNode *newNode = (TreeNode *)malloc(sizeof(TreeNode));
newNode->data = data;
newNode->color = RED; // 新增節點染成紅色
TreeNode *parent = NULL;
TreeNode *grandParent = NULL;
if (NULL == (*root)) {
//printf("插入根節點\n");
newNode->color = BLACK; // 將頭結點染成黑色
return newNode;
} else {
TreeNode *tmp = (*root);
while (tmp != NULL) {
//printf("插入查詢值: %d\n",tmp->data);
grandParent = parent;
parent = tmp;
if (data > tmp->data){
tmp = tmp->right;
} else if (data < tmp->data){
tmp = tmp->left;
} else {
printf("%d 該值已經存在\n",data);
}
}
if (data > parent->data) {
parent->right = newNode;
//printf("parent data:%d newNode data:%d\n",parent->data,newNode->data);
} else {
parent->left = newNode;
}
}
// 如果插入父節點是黑色,則保持不變
if (parent->color == BLACK) return (*root);
// 進行插入修復
return InsertFixup(root,newNode);
}
插入修復程式碼
PTreeNode InsertFixup(PTreeNode *root,PTreeNode newNode) {
TreeNode *parent = NULL; // 父親節點
TreeNode *grandParent = NULL; // 爺爺節點
TreeNode *greatGrandParent = NULL; // 祖爺爺節點
TreeNode *uncle = NULL; // 叔叔節點
int uncleColor = 0; // 叔叔節點顏色,預設為黑色
while (true) {
//
// 尋找節點 父親/爺爺祖爺爺節點
TreeNode *tmp = (*root);
while (tmp != NULL) {
if (newNode->data == tmp->data) {
break;
}
greatGrandParent = grandParent;
grandParent = parent;
parent = tmp;
(newNode->data > tmp->data) ? (tmp = tmp->right) : (tmp = tmp->left);
}
// 當前節點是根節點,染成黑色
if (newNode == (*root)) newNode->color = BLACK;
if (grandParent == NULL) break;
// 獲取叔叔節點
(parent == grandParent->left) ? (uncle = grandParent->right) : (uncle = grandParent->left);
// 獲取叔叔節點顏色
if (uncle != NULL) uncleColor = uncle->color;
// 染色操作
if ((parent->color == RED) && (uncleColor == RED)) {
parent->color = BLACK;
uncle->color = BLACK;
grandParent->color = RED;
newNode = grandParent;
continue;
}
// 父節點為紅色,叔叔節點為黑色,則需要進行旋轉
if ((parent->color == RED) && uncleColor == BLACK) {
// 左左
if ((parent == grandParent->left) && (newNode == parent->left)) {
grandParent->left = NULL;
TreeNode *oldParentRight = parent->right;
parent->right = grandParent;
grandParent->left = oldParentRight;
if ((*root) == grandParent) {
(*root) = parent;
} else {
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = parent) : (greatGrandParent->right = parent);
}
// 染色
newNode->color = RED;
grandParent->color = RED;
parent->color = BLACK;
//continue;
break;
// 左右
} else if ((parent == grandParent->left) && (newNode == parent->right)) {
grandParent->left = newNode;
TreeNode *newNodeLeft = newNode->left;
newNode->left = parent;
parent->right = newNodeLeft;
TreeNode *newNodeRight = newNode->right;
newNode->right = grandParent;
grandParent->left = newNodeRight;
//newNode->left = parent;
// 若要旋轉的樹是根節點
if ((*root) == grandParent) {
(*root) = newNode;
} else {
// 要旋轉的樹非根節點
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = newNode) : (greatGrandParent->right = newNode);
}
parent->color = RED;
grandParent->color = RED;
newNode->color = BLACK;
//continue;
break;
// 右左
} else if ((parent == grandParent->right) && (newNode == parent->left)) {
parent->left = NULL;
TreeNode *newNodeRight = newNode->right;
newNode->right = parent;
parent->left = newNodeRight;
grandParent->right = newNode;
TreeNode *newNodeLeft = newNode->left;
newNode->left = grandParent;
grandParent->right = newNodeLeft;
// 要旋轉的樹是根節點
if ((*root) == grandParent) {
(*root) = newNode;
} else {
// 要旋轉的樹是非根節點
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = newNode) : (greatGrandParent->right = newNode);
}
// 染色
newNode->color = BLACK;
grandParent->color = RED;
parent->color = RED;
//continue;
break;
// 右右
} else if ((parent == grandParent->right) && (newNode == parent->right)) {
grandParent->right = NULL;
TreeNode *oldParentLeft = parent->left;
parent->left = grandParent;
grandParent->right = oldParentLeft;
// 要旋轉的樹是根節點
if ((*root) == grandParent) {
(*root) = parent;
} else {
// 要旋轉的樹是非根節點
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = parent) : (greatGrandParent->right = parent);
}
// 染色
grandParent->color = RED;
newNode->color = RED;
parent->color = BLACK;
//continue;
break;
}
}
break;
}
return (*root);
}
插入完整程式碼
# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# define RED 1
# define BLACK 0
typedef struct {
int data; // 資料節點
struct TreeNode *left; // 左子樹
struct TreeNode *right; // 右字樹
int color; // 二叉樹顏色,1: 紅色 0:黑色
} TreeNode , *PTreeNode;
PTreeNode InsertFixup(PTreeNode *root,PTreeNode newNode) {
TreeNode *parent = NULL; // 父親節點
TreeNode *grandParent = NULL; // 爺爺節點
TreeNode *greatGrandParent = NULL; // 祖爺爺節點
TreeNode *uncle = NULL; // 叔叔節點
int uncleColor = 0; // 叔叔節點顏色,預設為黑色
while (true) {
//
// 尋找節點 父親/爺爺祖爺爺節點
TreeNode *tmp = (*root);
while (tmp != NULL) {
if (newNode->data == tmp->data) {
break;
}
greatGrandParent = grandParent;
grandParent = parent;
parent = tmp;
(newNode->data > tmp->data) ? (tmp = tmp->right) : (tmp = tmp->left);
}
// 當前節點是根節點,染成黑色
if (newNode == (*root)) newNode->color = BLACK;
if (grandParent == NULL) break;
// 獲取叔叔節點
(parent == grandParent->left) ? (uncle = grandParent->right) : (uncle = grandParent->left);
// 獲取叔叔節點顏色
if (uncle != NULL) uncleColor = uncle->color;
// 染色操作
if ((parent->color == RED) && (uncleColor == RED)) {
parent->color = BLACK;
uncle->color = BLACK;
grandParent->color = RED;
newNode = grandParent;
continue;
}
// 父節點為紅色,叔叔節點為黑色,則需要進行旋轉
if ((parent->color == RED) && uncleColor == BLACK) {
// 左左
if ((parent == grandParent->left) && (newNode == parent->left)) {
grandParent->left = NULL;
TreeNode *oldParentRight = parent->right;
parent->right = grandParent;
grandParent->left = oldParentRight;
if ((*root) == grandParent) {
(*root) = parent;
} else {
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = parent) : (greatGrandParent->right = parent);
}
// 染色
newNode->color = RED;
grandParent->color = RED;
parent->color = BLACK;
//continue;
break;
// 左右
} else if ((parent == grandParent->left) && (newNode == parent->right)) {
grandParent->left = newNode;
TreeNode *newNodeLeft = newNode->left;
newNode->left = parent;
parent->right = newNodeLeft;
TreeNode *newNodeRight = newNode->right;
newNode->right = grandParent;
grandParent->left = newNodeRight;
//newNode->left = parent;
// 若要旋轉的樹是根節點
if ((*root) == grandParent) {
(*root) = newNode;
} else {
// 要旋轉的樹非根節點
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = newNode) : (greatGrandParent->right = newNode);
}
parent->color = RED;
grandParent->color = RED;
newNode->color = BLACK;
//continue;
break;
// 右左
} else if ((parent == grandParent->right) && (newNode == parent->left)) {
parent->left = NULL;
TreeNode *newNodeRight = newNode->right;
newNode->right = parent;
parent->left = newNodeRight;
grandParent->right = newNode;
TreeNode *newNodeLeft = newNode->left;
newNode->left = grandParent;
grandParent->right = newNodeLeft;
// 要旋轉的樹是根節點
if ((*root) == grandParent) {
(*root) = newNode;
} else {
// 要旋轉的樹是非根節點
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = newNode) : (greatGrandParent->right = newNode);
}
// 染色
newNode->color = BLACK;
grandParent->color = RED;
parent->color = RED;
//continue;
break;
// 右右
} else if ((parent == grandParent->right) && (newNode == parent->right)) {
grandParent->right = NULL;
TreeNode *oldParentLeft = parent->left;
parent->left = grandParent;
grandParent->right = oldParentLeft;
// 要旋轉的樹是根節點
if ((*root) == grandParent) {
(*root) = parent;
} else {
// 要旋轉的樹是非根節點
(greatGrandParent->left == grandParent) ? (greatGrandParent->left = parent) : (greatGrandParent->right = parent);
}
// 染色
grandParent->color = RED;
newNode->color = RED;
parent->color = BLACK;
//continue;
break;
}
}
break;
}
return (*root);
}
PTreeNode Insert(PTreeNode *root,int data) {
printf("插入資料: %d\n",data);
TreeNode *newNode = (TreeNode *)malloc(sizeof(TreeNode));
newNode->data = data;
newNode->color = RED; // 新增節點染成紅色
TreeNode *parent = NULL;
TreeNode *grandParent = NULL;
if (NULL == (*root)) {
//printf("插入根節點\n");
newNode->color = BLACK; // 將頭結點染成黑色
return newNode;
} else {
TreeNode *tmp = (*root);
while (tmp != NULL) {
//printf("插入查詢值: %d\n",tmp->data);
grandParent = parent;
parent = tmp;
if (data > tmp->data){
tmp = tmp->right;
} else if (data < tmp->data){
tmp = tmp->left;
} else {
printf("%d 該值已經存在\n",data);
}
}
if (data > parent->data) {
parent->right = newNode;
//printf("parent data:%d newNode data:%d\n",parent->data,newNode->data);
} else {
parent->left = newNode;
}
}
// 如果插入父節點是黑色,則保持不變
if (parent->color == BLACK) return (*root);
// 進行插入修復
return InsertFixup(root,newNode);
}
// 前序遍歷
void Print1(TreeNode *root) {
if (NULL == root) return;
printf("[顏色:%s 資料:%d]\t",(root->color == RED) ? ("紅"):("黑") ,root->data);
Print1(root->left);
Print1(root->right);
}
// 中序遍歷
void Print2(TreeNode *root) {
if (NULL == root) return;
Print2(root->left);
printf("[顏色:%s 資料:%d]\t",(root->color == RED) ? ("紅"):("黑") ,root->data);
Print2(root->right);
}
int main() {
TreeNode *root = NULL;
int num[] = {160,130,210,150,110,90,80,85,240,260,248};
int i;
for (i=0;i<sizeof(num)/sizeof(int);i++) {
root = Insert(&root,num[i]);
}
printf("\n");
printf("前序遍歷:\n");
Print1(root);
printf("\n");
printf("\n");
printf("後續遍歷:\n");
Print2(root);
printf("\n");
return 0;
}
程式執行
# gcc RedBlackTreeInsert.c -w -g
# ./a.out
插入資料: 160
插入資料: 130
插入資料: 210
插入資料: 150
插入資料: 110
插入資料: 90
插入資料: 80
插入資料: 85
插入資料: 240
插入資料: 260
插入資料: 248
前序遍歷:
[顏色:黑 資料:130] [顏色:黑 資料:90] [顏色:黑 資料:80] [顏色:紅 資料:85] [顏色:黑 資料:110] [顏色:黑 資料:160] [顏色:黑 資料:150] [顏色:紅 資料:240] [顏色:黑 資料:210] [顏色:黑 資料:260] [顏色:紅 資料:248]
後續遍歷:
[顏色:黑 資料:80] [顏色:紅 資料:85] [顏色:黑 資料:90] [顏色:黑 資料:110] [顏色:黑 資料:130] [顏色:黑 資料:150] [顏色:黑 資料:160] [顏色:黑 資料:210] [顏色:紅 資料:240] [顏色:紅 資料:248] [顏色:黑 資料:260]
#
uploading-image-456399.png
歡迎轉發! 請保留源地址: https://www.cnblogs.com/NoneID