总结规律:
1、2-3-4树:新增元素+2节点合并(节点中只有1个元素)=3节点(节点中有2个元素)
红黑树:新增一个红色节点+黑色父亲节点=上黑下红(2节点---------------不要调整)
2、2-3-4树:新增元素+3节点合并(节点中有2个元素)=4节点(节点中有3个元素)
这里有6种小情况(左3,右3,还有2个左中右不需要调整)------左3,右3需要调整,其余2个不需要调整左3,右3(叔叔结点不存在):
还有2个左中右不需要调整
红黑树:新增红色节点+上黑下红=排序后中间节点是黑色,两边节点都是红色(3节点)注意:叔叔结点存在且为黑 且为左3或者右3结构 也需要进行调整(后面会总结讲到)
3、2-3-4树:新增一个元素+4节点合并=原来的4节点分裂,中间元素升级为父节点,新增元素与剩下的其中一个合并(叔叔结点存在且为红,需要进行调整)
红黑树:新增红色节点+爷爷节点黑,父节点和叔叔节点都是红色=爷爷节点变红,父亲和叔叔变黑,如果爷爷是根节点,则再变黑>>由以上的2-3-4树分析可得出需要调整的情况如下:
(一)左三(结点插在祖父节点的左子树)
1、“叔叔存在且为红”的时候,只需要进行颜色调整+继续向上调整2、若新增结点挂在父结点的左子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行一次“右旋”,再进行“颜色调整
3、若新增结点挂在父结点的右子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行两次“旋转”,先“左旋”后“右旋”,再进行“颜色调整”
(二)右三(结点插在祖父节点的右子树)
1、“叔叔存在且为红”的时候,只需要进行颜色调整+继续向上调整2、若新增结点挂在父结点的右子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行一次“左旋”,再进行“颜色调整”
3、若新增结点挂在父结点的左子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行两次“旋转”,先“右旋”后“左旋”,再进行“颜色调整”
>>这些情况后文会详解分析并给出核心代码
分左3,右3情况讨论:
何为调整?(颜色调整+旋转调整)
(一)左三(结点插在祖父节点的左子树)
1、“叔叔存在且为红”的时候,只需要进行颜色调整+继续向上调整,即把父亲结点和叔叔结点的颜色设置为BLACK,把祖父结点的颜色设置为RED(若祖父结点为根结点,则再变黑);
讨论可能出现的问题,新增结点加入之后,经过变色调整后,还需要继续向上调整
部分核心代码:
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
2、若新增结点挂在父结点的左子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行一次“右旋”,再进行“颜色调整”,把父结点颜色设置为BLACK,把祖父结点的颜色设置为RED
部分核心代码:
//2、若新增结点挂在父结点的左子树(前提),uncle不存在/存在且为黑进行统一调整
if (cur == p->left) {
//单旋
rightRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
3、若新增结点挂在父结点的右子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行两次“旋转”,先“左旋”后“右旋”,再进行“颜色调整”,把新增(当前)结点颜色设置为BLACK,把祖父结点的颜色设置为RED
分析规律:其实两次“旋转”,为了让第一次旋转操作可以恢复到2,就是祖父结点、父亲结点以及新增(当前)结点都在一条直线上。所以在这种情况下,可以先“左旋”,使之恢复到2,然后再进行和2一样的“右旋”操作。
部分核心代码:
//若新增结点挂在父结点的右子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行两次“旋转”,先“左旋”后“右旋”,再进行“颜色调整”
leftRotate(p);
rightRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
左三全部情况汇总如下:
(二)右三(结点插在祖父节点的右子树)
1、“叔叔存在且为红”的时候,只需要进行颜色调整+继续向上调整,即把父亲结点和叔叔结点的颜色设置为BLACK,把祖父结点的颜色设置为RED(若祖父结点为根结点,则再变黑);
讨论可能出现的问题,新增结点加入之后,经过变色调整后,还需要继续向上调整
部分核心代码:
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
2、若新增结点挂在父结点的右子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行一次“左旋”,再进行“颜色调整”,把父结点颜色设置为BLACK,把祖父结点的颜色设置为RED
部分核心代码:
//2、若新增结点挂在父结点的右子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行一次“左旋”,再进行“颜色调整”
if (cur == p->right) {
//单旋
leftRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
3、若新增结点挂在父结点的左子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行两次“旋转”,先“右旋”后“左旋”,再进行“颜色调整”,把新增(当前)结点颜色设置为BLACK,把祖父结点的颜色设置为RED
分析规律:其实两次“旋转”,为了让第一次旋转操作可以恢复到2,就是祖父结点、父亲结点以及新增(当前)结点都在一条直线上。所以在这种情况下,可以先“右旋”,使之恢复到2,然后再进行和2一样的“左旋”操作。
部分核心代码:
//若新增结点挂在父结点的左子树(前提),出现"叔叔不存在"或者“叔叔存在且为黑”,两种情况统一操作:先进行两次“旋转”,先“右旋”后“左旋”,再进行“颜色调整”
rightRotate(p);
leftRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
右三全部情况汇总如下:
左旋操作:
》》围绕p左旋:
①p是左孩子
pf pf
/ /
p pr(r)
/ \ / \
pl pr(r) ==> p rr(r)
/ \ / \
rl rr pl rl②p是右孩子
pf pf
\ \
p pr(r)
/ \ / \
pl pr(r) ==> p rr(r)
/ \ / \
rl rr pl rl
//左旋
void leftRotate(Node* p) {
Node* r = p->right;
Node* rl = r->left;
p->right = rl;
if (rl) {
rl->parent = p;
}
Node* parentParent = p->parent;
//(双向)
r->left = p;//p是r的左孩子
p->parent = r;//r是p的父亲
if (p == root) {//若p是根结点
root = r;
r->parent = nullptr;
}
else {
if (parentParent->left == p) { //p是左孩子
parentParent->left = r;
}
else { //p是右孩子
parentParent->right = r;
}
r->parent = parentParent;
}
}
右旋操作:
右旋
》》围绕p右旋:①p是左孩子
pf pf
/ /
p pl(l)
/ \ ==》 / \
pl(l) pr ll p
/ \ / \
ll lr lr pr②p是右孩子
pf pf
\ \
p pl(l)
/ \ ==> / \
pl(l) pr ll p
/ \ / \
ll lr lr pr
//右旋
void rightRotate(Node*& p) {
Node* l = p->left;
Node* lr = l->right;
p->left = lr;
if (lr) {
lr->parent = p;
}
Node* parentParent = p->parent;
l->right = p;
p->parent = l;
if (p == root) {
root = l;
root->parent = nullptr;
}
else {
if (parentParent->left == p) {
parentParent->left = l;
}
else {
parentParent->right = l;
}
l->parent = parentParent;
}
}
比较结点值的大小
int compareTo(const pair<k, v> kv, Node* cur) {
if (kv.first < cur->kv.first)
return -1;
else if (kv.first > cur->kv.first)
return 1;
else
return 0;
}
调整红黑树
void fixAfterPut(Node* p, Node* cur) {
while (p && p->color == RED) {
Node* grandfather = p->parent;
if (p == grandfather->left) {
Node* uncle = grandfather->right;
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
else {//2、uncle不存在/存在且为黑
if (cur == p->left) {
//单旋
rightRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
else {
//双旋
leftRotate(p);
rightRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
}
break;
}
}
else {
Node* uncle = grandfather->left;
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
else {//2、uncle不存在/存在且为黑
if (cur == p->right) {
//单旋
leftRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
else {
//双旋
rightRotate(p);
leftRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
}
break;
}
}
}
}
获取红黑树的高度
int Height()
{
return _Height(root);
}
int _Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = _Height(root->left);
int rightHeight = _Height(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
完整代码:
/*
1、2-3-4树:新增元素+2节点合并(节点中只有1个元素)=3节点(节点中有2个元素)
红黑树:新增一个红色节点+黑色父亲节点=上黑下红(2节点)--------------不要调整
2、2-3-4树:新增元素+3节点合并(节点中有2个元素)=4节点(节点中有3个元素)
这里有6种情况(左3,右3,还有2个左中右不需要调整)----------------左3,右3需要调整,其余2个不需要调整
红黑树:新增红色节点+上黑下红=排序后中间节点是黑色,两边节点都是红色(3节点)
3、2-3-4树:新增一个元素+4节点合并=原来的4节点分裂,中间元素升级为父节点,新增元素与剩下的其中一个合并
红黑树:新增红色节点+爷爷节点黑,父节点和叔叔节点都是红色=爷爷节点变红,父亲和叔叔变黑,如果爷爷是根节点,则再变黑
*/
#include<iostream>
using namespace std;
#include<vector>
#include<cmath>
#include<string>
#include<vector>
#define N 100
//#include "StringBuilder.h"
enum Colour {
RED,
BLACK
};
template<class k,class v>
struct RBTreeNode {
RBTreeNode<k, v>* left;
RBTreeNode<k, v>* right;
RBTreeNode<k, v>* parent;
pair<k, v> kv;
Colour color;
RBTreeNode(const pair<k, v>& kv)
:left(nullptr)
, right(nullptr)
, parent(nullptr)
, kv(kv)
, color(RED)
{}
};
template<class k, class v>
class RBTree {
typedef RBTreeNode<k, v> Node;
public:
RBTree()
:root(nullptr)
{}
//插入结点
bool Insert(const pair<k, v>& kv) {
if (root == nullptr) {
root = new Node(kv);
root->color = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = root;
int res;
while (cur) {
res = this->compareTo(kv, cur);
if (res == 1) {
parent = cur;
cur = cur->right;
}
else if (res == -1) {
parent = cur;
cur = cur->left;
}
else {
return false;
}
}
cur = new Node(kv);
cur->color = RED;//新增节点
res = this->compareTo(kv, parent);
//如果比较最终落在右子树,则直接将父节点右指针指向cur
if (res == 1) {
parent->right = cur;
cur->parent = parent;
}
//如果比较最终落在左子树,则直接将父节点左指针指向cur
else {
parent->left = cur;
cur->parent = parent;
}
//调整
fixAfterPut(parent,cur);
root->color = BLACK;
return true;
}
void fixAfterPut(Node* p,Node* cur) {
while (p && p->color == RED) {
Node* grandfather = p->parent;
if (p == grandfather->left) {
Node* uncle = grandfather->right;
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
else {//2、uncle不存在/存在且为黑
if (cur == p->left) {
//单旋
rightRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
else {
//双旋
leftRotate(p);
rightRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
}
break;
}
}
else {
Node* uncle = grandfather->left;
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
else {//2、uncle不存在/存在且为黑
if (cur == p->right) {
//单旋
leftRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
else {
//双旋
rightRotate(p);
leftRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
}
break;
}
}
}
}
int compareTo(const pair<k, v> kv, Node* cur) {
if (kv.first < cur->kv.first)
return -1;
else if (kv.first > cur->kv.first)
return 1;
else
return 0;
}
//左旋
/*
》》围绕p左旋:
①p是左孩子
pf pf
/ /
p pr(r)
/ \ / \
pl pr(r) ==> p rr(r)
/ \ / \
rl rr pl rl
②p是右孩子
pf pf
\ \
p pr(r)
/ \ / \
pl pr(r) ==> p rr(r)
/ \ / \
rl rr pl rl
*/
void leftRotate(Node* p) {
Node* r = p->right;
Node* rl = r->left;
p->right = rl;
if (rl) {
rl->parent = p;
}
Node* parentParent = p->parent;
//(双向)
r->left = p;//p是r的左孩子
p->parent = r;//r是p的父亲
if (p == root) {//若p是根结点
root = r;
r->parent = nullptr;
}
else {
if (parentParent->left == p) { //p是左孩子
parentParent->left = r;
}
else { //p是右孩子
parentParent->right = r;
}
r->parent = parentParent;
}
}
//右旋
/*
》》围绕p右旋:
①p是左孩子
pf pf
/ /
p pl(l)
/ \ ==》 / \
pl(l) pr ll p
/ \ / \
ll lr lr pr
②p是右孩子
pf pf
\ \
p pl(l)
/ \ ==> / \
pl(l) pr ll p
/ \ / \
ll lr lr pr
*/
void rightRotate(Node*& p) {
Node* l = p->left;
Node* lr = l->right;
p->left = lr;
if (lr) {
lr->parent = p;
}
Node* parentParent = p->parent;
l->right = p;
p->parent = l;
if (p == root) {
root = l;
root->parent = nullptr;
}
else {
if (parentParent->left == p) {
parentParent->left = l;
}
else {
parentParent->right = l;
}
l->parent = parentParent;
}
}
void InOrder()
{
_InOrder(root);
}
//中序遍历
void _InOrder(Node* root) {
if (root == nullptr) {
return;
}
_InOrder(root->left);
cout << root->kv.first << ":" << root->kv.second << endl;
_InOrder(root->right);
}
/*
检测是否满足红黑树的性质:任意选取一条路径找出黑色结点的个数作为基准值,然后依次与其他
路径比较,如果都相同则满足红黑树的性质。
*/
bool IsBalance() {
if (root && root->color == RED) {
cout << "根结点不是黑色" << endl;//为了测试
return false;
}
//最左路径黑色结点数量做基准值
int banchmark = 0;//保存最左侧路径中黑色结点的个数
Node* left = root;
while (left) {
if (left->color == BLACK)
++banchmark;
left = left->left;
}
int blackNum = 0;//递归遍历时记录黑色结点的个数
return _IsBalance(root, banchmark, blackNum);
}
bool _IsBalance(Node* root, int banchmark, int blackNum)
{
//走到null之后,判断banchmark和blackNum是否相等
if (root == nullptr)
{
if (banchmark != blackNum)
{
cout << "存在路径黑色结点的数量不相等" << endl;//为了测试
return false;
}
return true;
}
if (root->color == RED && root->parent->color == RED)
{
cout << "出现了连续的红色结点" << endl;//为了测试
return false;
}
if (root->color == BLACK)// 统计黑色节点的个数
{
++blackNum;
}
return _IsBalance(root->left, banchmark, blackNum)
&& _IsBalance(root->right, banchmark, blackNum);
}
int Height()
{
return _Height(root);
}
int _Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = _Height(root->left);
int rightHeight = _Height(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
Node* getRoot() {
return this->root;
}
private:
Node* root;
};
template<class k, class v>
class TreeOperation {
typedef RBTreeNode<k, v> Node;
/*
树的结构示例:
1
/ \
2 3
/ \ / \
4 5 6 7
*/
// 用于获得树的层数
public:
int getTreeDepth(Node* root) {
if (root == nullptr)
return 0;
else
{
int ldep = getTreeDepth(root->left);
int rdep = getTreeDepth(root->right);
return (ldep < rdep) ? rdep + 1 : ldep + 1;
}
}
void writeArray(Node* currNode, int rowIndex, int columnIndex, string** res, int treeDepth) {
// 保证输入的树不为空
if (currNode == nullptr) return;
// 0、默认无色
//res[rowIndex][columnIndex] = String.valueOf(currNode.getValue());
//1、颜色表示
if (currNode->color == BLACK) {//黑色,加色后错位比较明显
res[rowIndex][columnIndex] = ("\033[40;3m" + currNode->kv.second + "\033[0m");
//cout << "-----------------------------------------" << res[rowIndex][columnIndex] << endl;
}
else {
res[rowIndex][columnIndex] = ("\033[31;3m" + currNode->kv.second + "\033[0m");
//cout << "=========================================" << res[rowIndex][columnIndex] << endl;
}
//2、R,B表示
//res[rowIndex][columnIndex] = String.valueOf(currNode.getValue()+"-"+(currNode.isColor()?"B":"R")+"");
// 计算当前位于树的第几层
int currLevel = ((rowIndex + 1) / 2);
// 若到了最后一层,则返回
if (currLevel == treeDepth) return;
// 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
int gap = treeDepth - currLevel - 1;
// 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
if (currNode->left != nullptr) {
res[rowIndex + 1][columnIndex - gap] = "/";
writeArray(currNode->left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
}
// 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
if (currNode->right != nullptr) {
res[rowIndex + 1][columnIndex + gap] = "\\";
writeArray(currNode->right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
}
}
void show(Node* root) {
if (root == nullptr) printf("EMPTY!");
//printf("==============");
// 得到树的深度
int treeDepth = getTreeDepth(root);
// 最后一行的宽度为2的(n - 1)次方乘3,再加1
// 作为整个二维数组的宽度
int arrayHeight = treeDepth * 2 - 1;
int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
/*printf("==============%d \n", arrayHeight);
printf("==============%d \n", arrayWidth);*/
// 用一个字符串数组来存储每个位置应显示的元素
//string res[arrayHeight][arrayWidth];
int rows = arrayHeight;
int cols = arrayWidth;
//动态创建二维字符数组arr[m][n]
string** res;
res = new string * [arrayHeight]; //创建行指针
for (int i = 0; i < arrayHeight; i++)
res[i] = new string[arrayWidth]; //为每行分配空间
// 对数组进行初始化,默认为一个空格
for (int i = 0; i < arrayHeight; i++)
{
for (int j = 0; j < arrayWidth; j++)
{
res[i][j] = " ";
}
}
// 从根节点开始,递归处理整个树
writeArray(root, 0, arrayWidth / 2, res, treeDepth);
for (int i = 0; i < arrayHeight; i++)
{
vector<string> strVector = {};
for (int j = 0; j < arrayWidth; j++) {
cout<< res[i][j];
int tempStrLen = res[i][j].length();
if (tempStrLen > 1 && j <= arrayHeight - 1) {
j += tempStrLen > 4 ? 2 : tempStrLen - 1;
}
//strVector.push_back(res[i][j]);
/*if (res[i][j].length() > 1 && j <= arrayHeight - 1) {
j += res[i][j].length() > 4 ? 2 : res[i][j].length() - 1;
}*/
}
cout << endl;
/*string str="";
for (vector<string>::const_iterator it = strVector.begin(); it != strVector.end(); it++)
{
str.append(*it);
}
cout << str << endl;*/
}
}
};
void insertOpt() {
RBTree<string, string> t;
int inputV = 0;
string str = "";
while (true) {
printf("请输入你要插入的节点:");
scanf_s("%d", &inputV);
str = to_string(inputV);
//这里代码最多支持3位数,3位以上的话红黑树显示太错位了,这里就不重构代码了,大家可自行重构
if (str.length() == 1) {
str = "00" + str;
}
else if (str.length() == 2) {
str = "0" + str;
}
t.Insert(make_pair(str, str));
TreeOperation<string, string> Tp;
Tp.show(t.getRoot());
}
}
void TestRBTree()
{
//RBTree<int, int> t;
RBTree<string, string> t;
//string a[] = { "5","4","3","2","1","0"};
//string a[] = { "16","3","7","11","9","26","18","14","15"};
//string a[] = { "4","2","6","1","3","5","15","7","16","14"};
//string a[] = { "4","2","6","1","3","5","15","7","16","14","8","9","10","11","12","13","14"};
string a[] = { "6","4","10","8","3","7","9"};
for (auto str : a)
{
if (str.length() == 1) {
str = "00" + str;
}
else if (str.length() == 2) {
str = "0" + str;
}
t.Insert(make_pair(str, str));
//cout << "Insert" << e << ":" << t.IsBalance() << endl;
}
/*t.InOrder();
cout << t.IsBalance() << endl;
cout << t.Height() << endl;*/
//TreeOperation<int, int> Tp;
TreeOperation<string, string> Tp;
Tp.show(t.getRoot());
}
int main() {
/*TestRBTree();
cout << endl;
cout << endl;
cout << endl;*/
insertOpt();
system("pause");
return 0;
}
效果展示如下: