轉自我自己的博客:https://blog.laclic.ink/2020/12/25/RBtree/
思路來源:Wechat article.
int 型 紅黑樹
采用了雙向結構,子節點可指向父節點
代碼采用了多.c文件形式,需要在一個project下進行編譯
函數名 | 英文釋義 | 中文釋義 |
---|---|---|
treeCrt() | Tree Create | 創建樹 |
treeAdd() | Tree Add | 向樹添加節點 |
treePrt() | Tree Print | 打印樹/輸出樹 |
\ | \ | \ |
_treeAdd_recur() | Tree Add Recursion | 通過遞歸方式查找插入位點並插入 |
_treeJug() | Tree Judge | 比較判斷節點值的大小關系 |
_treePrt_recur() | Tree Print Recursion | 通過遞歸方式打印樹 |
_treeChk() | Tree Check | 檢查節點附近的情況 |
_treeRot() | Tree Rotate | 選擇樹的結點 |
_nodeCrt() | Node Create | 創建並返回一個帶值的節點 |
注:開頭帶下划線的函數表示私有函數(不希望直接被訪問,只是為了給非下划線函數使用方便)
頭文件:
#ifndef _TREE_H_
#define _TREE_H_
#include <stdio.h>
#include <stdlib.h>
typedef struct _node
{
int value;
struct _node *p[2]; // p[0]: left-son, p[1]: right-son
struct _node *father;
char BR; // 0: black; 1: red;
}Node;
typedef struct _rbtree
{
Node* head;
}RBTree;
RBTree treeCrt();
void treeAdd(RBTree* t,int value);
void treePrt(RBTree t);
void _treeAdd_recur(Node* now,const int value);
void _treePrt_recur(Node* now,int layer);
void _treeChk(Node* now,int son);
int _treeJdg(Node* const now,const int value);
void _treeRot(Node* now,const int drc); // lift the left or right one to now position, drc means direction
Node* _nodeCrt(Node* father,int value);
#endif
紅黑樹 的 創建 與 添加
函數名 | 英文釋義 | 中文釋義 |
---|---|---|
treeCrt() | Tree Create | 創建樹 |
treeAdd() | Tree Add | 向樹添加節點 |
_treeAdd_recur() | Tree Add Recursion | 通過遞歸方式查找插入位點並插入 |
_treeJug() | Tree Judge | 比較判斷節點值的大小關系 |
_nodeCrt() | Node Create | 創建並返回一個帶值的節點 |
#include "tree.h"
RBTree treeCrt() {
RBTree t = {NULL};
return t;
}
#include "tree.h"
void treeAdd(RBTree*t,int value) { // 將添加函數進行了封裝,而非直接用主函數來進行遞歸
if(t->head) _treeAdd_recur(t->head,value);
else t->head = _nodeCrt(NULL,value);
t->head->BR = 0;
}
void _treeAdd_recur(Node* now,int value) {
int drc = _treeJdg(now,value); // drc: direction, left(0) or right(1) // _treeJdg()函數用於判斷大小以確定
if(now->p[drc]) _treeAdd_recur(now->p[drc],value);
else {
now->p[drc] = _nodeCrt(now,value);
_treeChk(now,drc); // 用於檢查紅黑關系的函數
}
}
#include "tree.h"
int _treeJdg(Node* const now,const int value) {
return now->value < value;
}
#include "tree.h"
Node* _nodeCrt(Node* father,int value) {
Node* node = (Node*) malloc(sizeof(Node));
node->father = father;
node->p[0] = NULL;
node->p[1] = NULL;
node->value = value;
node->BR = 1;
}
紅黑樹 的 打印/輸出/展示
由於輸出的特性,我們無法豎着輸出我們所熟悉的二叉樹,只能通過遞歸的方式來橫向輸出我們的二叉樹
函數名 | 英文釋義 | 中文釋義 |
---|---|---|
treePrt() | Tree Print | 打印樹/輸出樹 |
_treePrt_recur() | Tree Print Recursion | 通過遞歸方式打印樹 |
#include "tree.h"
void treePrt(RBTree t) { // 同樣是進行了封裝
if(t.head) _treePrt_recur(t.head,0); // 判斷空樹,如果t上有結點(不是空樹)就進入遞歸
else printf("This is an empty tree.\n"); // 否則,輸出空樹
printf("------------------------------------\n"); // 分隔符
}
void _treePrt_recur(Node* now,int layer) { // layer 代指當前所處的層數/遞歸的深度
if(now->p[1]) _treePrt_recur(now->p[1],layer+1);
for(int i=0;i<layer;++i) printf("\t"); // 制表符,讓格式更好看
printf("%d(BR-%d)\n",now->value,now->BR);
if(now->p[0]) _treePrt_recur(now->p[0],layer+1);
}
紅黑樹 的 檢查與旋轉
函數名 | 英文釋義 | 中文釋義 |
---|---|---|
_treeChk() | Tree Check | 檢查節點附近的情況 |
_treeRot() | Tree Rotate | 選擇樹的結點 |
檢查新插入節點、其父節點,以及其祖父節點(父節點的父節點)
#include "tree.h"
void _treeChk(Node* f,int son) { // son of father(f) is the added node
int status = 0b00; // if (son > f) ==> 0b1??, son > gf ==> 0b?1?
Node* gf = f->father;
Node* s; // s means son, Node*
if(gf&&f->BR) {
if(gf->p[0]&&gf->p[1]&&gf->p[0]->BR&&gf->p[1]->BR) {
// left-son and right-son exists, left and right sons are both red
gf->BR = 1;
gf->p[0]->BR = 0;
gf->p[1]->BR = 0;
if(gf->father) {
_treeChk(gf->father,gf->value > gf->father->value);
}
}else {
status = son;
s = f->p[son]; // s means son of f, Node*
status = (status << 1) + (f->p[son]->value > gf->value);
switch(status) {
case 0b00 : ;
case 0b11 :
f->BR = 1;
gf->BR = 0;
_treeRot(gf,!(1&status));
break;
case 0b10 : ;
case 0b01 :
f->BR = 1;
gf->BR = 0;
_treeRot(f,!son);
_treeRot(gf,!(1&status));
break;
default : ;
}
}
}
return;
}
以下函數中的drc,0代表左旋,1代表右旋
#include "tree.h"
void _treeRot(Node* now,const int drc) { // left-rotate: 0
now->value ^= now->p[!drc]->value ^= now->value ^= now->p[!drc]->value; // swap value
Node* temp = now->p[drc];
now->p[drc] = now->p[!drc];
now->p[!drc] = now->p[drc]->p[!drc];
now->p[drc]->p[!drc] = now->p[drc]->p[drc];
now->p[drc]->p[drc] = temp;
if(temp) temp->father = now->p[drc];
if(now->p[!drc]) now->p[!drc]->father = now;
}