c语言手搓int红黑树(2020.12.25更新至节点添加)
转自我自己的博客:https://blog.laclic.top/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;
}
Living with bustle, hearing of isolation.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!