1. 程式人生 > 其它 >紅黑樹 插入操作 C語言實現

紅黑樹 插入操作 C語言實現

紅黑樹 插入操作 C語言實現

推薦一個紅黑樹視覺化網站: https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

紅黑樹插入核心點:

如何判斷需要染色修復
插入節點的父節點為紅色,叔叔節點為紅色,則需要進行染色操作。

  1. 父親節點叔叔節點 染成黑色
  2. 將爺爺節點染成紅色
  3. 爺爺節點賦值給當前節點再次獲取父親/叔叔/爺爺節點進行判斷。

如何判斷需要旋轉修復

插入節點的父節點為紅色,叔叔節點為黑色,則需要進行旋轉修復

旋轉修復

左左修復/左右修復/右左修復/右右修復

1. 什麼是紅黑樹

https://baike.baidu.com/item/紅黑樹/2413209?fr=aladdin

紅黑樹是一種自平衡二叉查詢樹,巴拉巴拉--- , 請注意前提,是一顆二叉查詢樹

關於二叉查詢樹: https://baike.baidu.com/item/二叉排序樹/10905079?fromtitle=二叉查詢樹&fromid=7077965&fr=aladdin

2. 紅黑樹概念

  • 每個節點要麼是紅色,要麼是黑色
  • 根節點和葉節點是黑色的(紅黑樹的葉節點是NULL LEAF,預設NULL節點為黑色)
  • 如果一個節點是紅色,那麼它的孩子是黑色的(不允許連續2個紅節點,但是允許2個連續的黑節點)
  • 任意節點到葉節點的樹鏈中包含相同數量的黑節點

紅黑樹圖示

3. 紅黑樹插入

這篇部落格插入節點名稱規則

G: 爺爺節點
P: 父親節點
U: 叔叔節點
N: 新插入節點

3.1 插入規則

  • 1 . 對於新插入的節點,顏色初始化為紅節點。
  • 2 . 根節點為黑色。
  • 3 . 如果在黑節點後插入,則無需進行修復。
  • 4 . 如果在紅節點後插入,需要進行修復。

3.2 針對上面插入規則第三點 修復規則

  • 1 . 當前節點為根節點,且為紅色,需要染成黑色。
  • 2 . 若插入節點父節點為紅色,叔叔節點為紅色,則需要進行染色操作。
  • 3 . 若插入節點父節點為紅色,叔叔節點為黑色,則需要進行旋轉。

如何判斷需要染色修復

插入節點的父節點為紅色,叔叔節點為紅色,則需要進行染色操作。

染色修復

  1. 父親節點叔叔節點
    染成黑色
  2. 將爺爺節點染成紅色
  3. 爺爺節點賦值給當前節點再次獲取父親/叔叔/爺爺節點進行判斷。

如何判斷需要旋轉修復

插入節點的父節點為紅色,叔叔節點為黑色,則需要進行旋轉修復

旋轉修復

左左修復/左右修復/右左修復/右右修復

3.3 顏色修復

3.3.1 在黑節點後插入,則無需進行修復。

對於如下資料,我們插入一個節點 130

插入節點後如下,因為 130 的父節點為 黑節點,所以 無需進行修復

3.3.2 在紅節點後插入,需要進行修復

對於如下資料,我們插入一個150

插入之後資料如下

此時,P節點和N節點 都為紅節點,則需要進行修復

3.3.3 染色修復

父親節點叔叔節點 都為紅節點 需要進行染色修復

如上圖,染色修復流程為:

  1. 父親節點叔叔節點 染成黑色
  2. 將爺爺節點染成紅色
  3. 爺爺節點賦值給當前節點再次獲取父親/叔叔/爺爺節點進行判斷。

應該修復為:

且因N為根節點,應該染成黑色,故

4 旋轉修復

針對於修復,有如下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]
# 
歡迎轉發! 請保留源地址: https://www.cnblogs.com/NoneID