一、二叉查找树
BST:Binary Search Tree
特点:左子树节点比父节点小,右子树节点比父节点大。
二、平衡二叉树
在二叉查找树的基础上,每个节点左右子树高度差不能超过1。
添加子节点时,平衡二叉树可能变得不平衡,此时可通过单自旋或双旋来保持平衡。
平衡二叉树插入节点后变得不平衡,有以下四种情景
- 在当前节点的左子树的左子树插入元素
- 在当前节点的左子树的右子树插入元素
- 在当前节点的右子树的左子树插入元素
- 在当前节点的右子树的右子树插入元素
1、左旋
前两种场景,导致的结果都是左子树比右子树层级高超过1,产生不平衡,为解决不平衡问题,需要将当前节点的左子树上浮一个层级,当前节点下沉一个层,使得二叉树变平衡。这种操作称为左旋,把左子树旋转上来,当前节点下沉为左子树的右子树,并左子树的右子树挂载到当前节点的左子树。
对于第一种场景,直接旋转一次即可。
对于第种场景,需要旋转二次才能达到平衡
2、右旋
右旋场景与左旋场景相似不再赘述。
实现源码
#include<iostream>
#include <algorithm>
class Node {
public:
Node(int value){
this->value=value;
left=nullptr;
right=nullptr;
depth = 0;
}
Node *left;
Node *right;
int value;
//树的层级
int depth;
};
typedef void (*Callback)(Node* node);
class BinaryTree {
public:
BinaryTree(){}
void addNode(int value){
if(root==nullptr) {
root=new Node(value);
root->depth=0;
}
else
addNode(root, value);
}
Node* search(int value){
return search2(root, value);
}
/**
* 深度优化遍历
*/
void dump(){
dump(root);
}
private:
/**
* 先序遍历:中、左、右
* 中序遍历:左、中、右
* 后序遍历:左、右、中
* 根据平衡二叉树特点,需要使用中序遍历
*/
void travel(Node *node, Callback callback){
// std::cout<<"travel:"<<node->value<<std::endl;
//step1 处理左子树
if(node->left != nullptr)
travel(node->left, callback);
//step2 处理当前节点
callback(node);
//step3 处理右子树
if(node->right != nullptr)
travel(node->right, callback);
}
Node* search2(Node* node, int value){
if(node==nullptr)
return nullptr;
if(value == node->value)
return node;
if(value > node->value)
return search2(node->right, value);
else
return search2(node->left, value);
}
/**
* 添加节点
* 因指针值可能会被修改,这里使用指针引用
*/
void addNode(Node*& node, int value){
if(node->value==value)
return;
if(value < node->value){
if(node->left==nullptr)
{
Node* left = new Node(value);
node->left = left;
std::cout<<"add left item:"<<std::endl;
} else {
addNode(node->left, value);
if(getNodeDepth(node->left) - getNodeDepth(node->right)>1){
std::cout<<"left roate..."<<std::endl;
if(value < node->left->value){
Node* tmp = node;
node = leftOneceRoate(node);
tmp->depth = std::max(getNodeDepth(tmp->left),getNodeDepth(tmp->right));
}
else{
Node* tmp = node;
node = leftDoubleRoate(node);
tmp->depth = std::max(getNodeDepth(tmp->left),getNodeDepth(tmp->right));
}
}
}
node->depth=std::max(getNodeDepth(node->left), getNodeDepth(node->right))+1;
}else{
if(node->right==nullptr)
{
Node* right=new Node(value);
node->right=right;
std::cout<<"add right item:"<<value<<std::endl;
}
else{
//添加右子树,需要检查
addNode(node->right, value);
if(getNodeDepth(node->right)-getNodeDepth(node->left) > 1) {
if(value > node->right->value){
Node* tmp = node;
node = rightOnceRoate(node);
tmp->depth = std::max(getNodeDepth(tmp->left),getNodeDepth(tmp->right));
}
else{
Node* tmp = node;
node = rightDoubleRoate(node);
tmp->depth = std::max(getNodeDepth(tmp->left),getNodeDepth(tmp->right));
}
}
}
node->depth=std::max(getNodeDepth(node->left), getNodeDepth(node->right))+1;
}
}
/**
* 左旋一次
*/
Node* leftOneceRoate(Node* node){
std::cout<<"left once roate..."<<std::endl;
Node *tmp = node;
node = node->left;
tmp-> left = node->right;
node->right = tmp;
node->depth=std::max(getNodeDepth(node->left), getNodeDepth(node->right));
tmp->depth=std::max(getNodeDepth(tmp->left), getNodeDepth(tmp->right));
return node;
}
/**
* 左旋,旋两次
* 第一次,将当前节点左子节点的右节点旋转到当前节点的子节点,形成左旋一次的结构,
* 第二次,再左旋一次即可完成树的平衡
*/
Node* leftDoubleRoate(Node* node){
std::cout<<"left double roate..."<<std::endl;
//step1 第一次旋转
//要下沉的节点
Node* tmp1 = node->left;
//要上浮的节点
Node* tmp2 = node->left->right;
Node* tmp2Left = tmp2->left;
//tmp2 节点上浮
node->left = tmp2;
// 将下沉节点放到上浮节点的left
tmp2->left = tmp1;
// 将上浮节点的left挂载到下沉节点的right
tmp1->right = tmp2Left;
//step2 第二次旋转
return leftOneceRoate(node);
}
Node* rightOnceRoate(Node* node){
std::cout<<"right once roate..."<<std::endl;
Node* tmp=node;
node = node->right;
tmp-> right = node->left;
node->left = tmp;
return node;
}
Node* rightDoubleRoate(Node* node){
std::cout<<"right double roate..."<<std::endl;
Node* tmp1 = node->right;
Node* tmp2 = tmp1->left;
node->right = tmp2;
tmp1->left = tmp2->right;
tmp2->right = tmp1;
return rightOnceRoate(node);
}
int getNodeDepth(Node* node){
if(node==nullptr)
return 0;
int depth=node->depth;
if(node->left == nullptr && node->right == nullptr)
return depth;
int leftDepth = getNodeDepth(node->left);
int rightDepth = getNodeDepth(node->right);
if(leftDepth>rightDepth)
return leftDepth+1;
else
return rightDepth+1;
}
void dump(Node* node){
if(node==nullptr)
{
std::cout<<"Binary Three is empty."<<std::endl;
return;
}
travel(node, [](Node* item){
std::cout<<item->value<<"["<<item->depth<<"], ";
});
std::cout<<std::endl;
}
/**
* 树的节点
*/
Node* root=nullptr;
};
void testLeftRoate(BinaryTree *&tree){
tree->addNode(8);
tree->addNode(5);
tree->addNode(9);
tree->addNode(3);
tree->addNode(7);
// tree->addNode(4);
tree->addNode(6);
}
void testRightRoate(BinaryTree *tree) {
tree->addNode(4);
tree->addNode(2);
tree->addNode(7);
tree->addNode(5);
tree->addNode(9);
// tree->addNode(8);
tree->addNode(6);
}
int main(){
BinaryTree *tree = new BinaryTree();
testRightRoate(tree);
tree->dump();
}
三、2-3树
2-3树的特点是:树的节点可能有2到3个子树
2- 节点:节点只有1个值,2个子树。左子树所有节点都小于当前节点;右子树所有节点都大于当前节点。
3- 节点:节点有2个值,3个子树。左值小于右值,左子树所有节点都小于左值;右子树所有节点都大于右值;中间子树所有节点的值都处于左值和右值之间。
添加节点
原则:先将一个节点的能存储的存满(2- 树可存储1个值,3- 树可存储2个值,如果是2- 树可再存储一个值,从而变成3- 树),不能存储时(当前节点是3- 树,不能再存储了),裂变(3- 树拆成2个2- 树,并与新节点结合成一个新树)。
实现过程要优先判断要插入的元素是否处于当前节点的子树,如果位于子树上(子树不为空)则递归处理;不位于子树上(子树为空)则根据具体情况进行处理。
- 处于子树上。交给子树处理,且需判断子树是否升级,如果升级,需将子树根节点提升到当前节点,判断当前节点是否满足2-3性质,不满足情况下还需再次拆分。
- 不处于子树。如果当前节点是2-节点,直接变成3-节点,如果是3-节点,需要拆分成2个2-节点,升高一层级,升高的这一层要与原上层元素进行合并,如果原上层是2- 节点,直接变成3- 节点,如果是3- 节点还需要继续裂变。
- 要插入元素小于当前节点的第一个元素值,且当前节点的左子树为空
- 要插入元素大于当前节点的第二个元素值,且当前节点的右子树为空
- 要插入元素大于当前节点第一个元素,小于当前节点第二个元素,且中间子树为空
- 要插入元素大于当前节点第一个元素,当前节点第二个元素不存在,且右子树为空
#include<iostream>
class Node {
public:
Node(int value){
left=nullptr;
right=nullptr;
center=nullptr;
value1=value;
value2=0;
}
Node *left;
Node *right;
Node *center;
int value1;
int value2;
};
class TwoThreeTree {
public:
TwoThreeTree(){}
void addNode(int value){
if(root == nullptr){
root = new Node(value);
}else{
addNode(root, value);
}
}
/**
* 深度优化遍历
*/
void dump(){
dump(root);
std::cout<<std::endl;
}
private:
/**
* 添加节点
* 因指针值可能会被修改,这里使用指针引用
* 返回1 当前节点需要调用
* 返回0 当前节点不需要调整
* 添加节点时,优先查找,
*/
int addNode(Node*& node, int value){
if(value < node->value1){
if(node->left == nullptr){
//2-节点,左子树为空,且小于当前元素:优先将2-节点变成3-节点
if(node->value2==0){
node->value2 = node->value1;
node->value1 = value;
}
//如果是3-节点,裂变成2个2-节点
else{
Node* left = new Node(value);
left->right = node->left;
Node* right = new Node(node->value2);
right->left = node->left;
right->right = node->right;
node->left = left;
node->right = right;
node->value2 = 0;
node->center = nullptr;
return 1;
}
return 0;
}else{
//2-节点,左子树不为空,且小于当前元素:优化进行查找
int result = addNode(node->left, value);
if(result==0)
return 0;
//当前为2- 节点,直接变成3-节点
if(node->value2==0){
Node* tmp = node->left;
node->value2 = node->value1;
node->value1 = tmp->value1;
node->left = tmp->left;
node->center = tmp->right;
return 0;
}
Node* left=node->left;
Node* right = new Node(node->value2);
right->left = node->center;
right->right = node->right;
node->left = left;
node->right = right;
node->value2=0;
node->center=nullptr;
return 1;
}
}
//虽然大于value1,还需要判断value2是否为0,即当前节点是否是2-节点,如果是2-节点,直接将其变成3-节点即可
if(node->value2 == 0){
node->value2=value;
return 0;
}
// value1 < value < value2
if(value < node->value2){
if(node->center==nullptr){
Node* left = new Node(node->value1);
left->left = node->left;
// left->right = center->left;
Node* right = new Node(node->value2);
// right->left = center->right;
right->right = node->right;
node->value1 = value;
node->left = left;
node->right = right;
node->value2=0;
node->center = nullptr;
return 1;
}else{
int result = addNode(node->center, value);
if(result==0)
return 0;
Node* center = node->center;
Node* left = new Node(node->value1);
left->left = node->left;
left->right = center->left;
Node* right = new Node(node->value2);
right->left = center->right;
right->right = node->right;
node->value1 = center->value1;
node->left = left;
node->right = right;
node->value2=0;
node->center = nullptr;
// free(center);
return 1;
}
}
// 大于3-节点的 2元素
else{
if(node->right==nullptr){
Node* left = new Node(node->value1);
left->left=node->left;
left->right= node->center;
Node* right = new Node(value);
right->left = node->right;
node->value1= node->value2;
node->left=left;
node->right = right;
node->value2=0;
node->center=nullptr;
return 1;
}else{
int result = addNode(node->right, value);
if(result==0)
return 0;
//node同3-树变成了2-树
Node* right = node->right;
Node* left = new Node(node->value1);
left->left=node->left;
left->right=node->center;
// 将node从3-树变成2-树
node->value1 = node->value2;
node->left = left;
node->right = right;
node->center = nullptr;
node->value2 = 0;
return 1;
}
}
}
void dump(Node* node){
if(node==nullptr) return;
dump(node->left);
std::cout<<node->value1<<",";
if(node->value1==0)
{
std::cout<<"value2:"<<node->value2<<std::endl;
}
if(node->value2==0)
dump(node->right);
else{
dump(node->center);
std::cout<<node->value2<<",";
dump(node->right);
}
}
/**
* 树的节点
*/
Node* root=nullptr;
};
int main(){
TwoThreeTree *tree = new TwoThreeTree();
tree->addNode(8);
tree->addNode(6);
tree->addNode(12);
tree->addNode(3);
tree->addNode(4);
tree->addNode(7);
tree->addNode(9);
tree->addNode(5);
tree->addNode(1);
tree->addNode(2);
tree->dump();
}
四、红黑二叉树
红黑二叉树特性:
1、根节点为黑色。
2、所有叶子节点为黑色(叶子节点是nil节点)。
3、从每个叶子节点到根节点,不能有两个连续红色节点。
4、从任意节点到叶子节点,黑色节点的数目都相同(即黑高)。
添加方式:
1、按二叉查找树方式添加新节点,新节点颜色为红色。
2、如果父节点为黑色,无需处理;如果父节点为红色,需分情况处理
父节点红色,叔节点红色
。直接变色:将父节点与叔节点变成黑色,祖父节点变成红色。如果祖父节点为根节点,再将祖父节点变成黑色即可;如果祖父节点的父节点也为红色,需回退到祖父节点,再次作分情况处理。父节点为红色,叔节点为黑色或不存在
。此时需要区分当前节点、父节点、祖父节点是否在一条直线上。三点在一条直线
。 此时只需通过左旋或右旋将父节点升级一个层级,祖父节点降低一个层级,再改变父节点、祖父节点的颜色即可。三点不在一条直线
。此时需要两步操作,第一步将父节点与当前节点进行旋转(左旋或右旋),使得当前节点、父节点、祖父节点在一条直线,再按三点在一条直线场景,再旋转一次即可。
#include<iostream>
enum Color
{
RED,BLACK
};
class Node
{
public:
Node(int value){
this->value = value;
parent = nullptr;
left = nullptr;
right = nullptr;
}
Node *parent;
Node *left;
Node *right;
int value = 0;
Color color = RED;
};
class RedBlackTree {
public:
void addNode(int value){
if(root == nullptr){
root = new Node(value);
root->color = BLACK;
return;
}
Node *node = addNodeInner(root, value);
checkNode(node);
}
void dump(){
dump(root);
std::cout << std::endl;
}
private:
/**
* 返回新添加的节点
*/
Node* addNodeInner(Node* node, int value){
if(value < node->value){
if(node->left == nullptr)
{
Node *newNode = new Node(value);
newNode->parent = node;
node->left = newNode;
std::cout << "add new left node..." << std::endl;
return newNode;
}
return addNodeInner(node->left, value);
} else {
if(node->right == nullptr){
Node *newNode = new Node(value);
newNode->parent = node;
node->right = newNode;
return newNode;
}
return addNodeInner(node->right, value);
}
}
void checkNode(Node*& node){
//父节点为黑色,无需处理
if(node->parent->color == BLACK)
return;
//父节点为红色,祖父节点肯定是存在的,因为根节点只能是黑色;祖父节点肯定是黑色,因为不能有两个红色节点相连
Node *parent = node->parent;
Node *grandfather = parent->parent;
Node *uncle = nullptr;
int parentDir = 0;
int nodeDir = node->value < parent->value ? 1 : 2;
if (parent->value < grandfather->value)
{
uncle = grandfather->right;
parentDir = 1;
}
else
{
uncle = grandfather->left;
parentDir = 2;
}
//叔节点存在,且为红色:父节点、叔节点变成黑色,祖父节点变成红色。如果祖父节点为根节点,再将其变成黑色
if(uncle != nullptr && uncle->color == RED){
parent->color = BLACK;
uncle->color = BLACK;
if(grandfather != root) {
grandfather->color = RED;
//再检测grandfather节点是否满足条件
checkNode(grandfather);
}
return;
}
// uncle==nullptr || uncle->color == BLACK
//当前节点、父节点、祖父节点在一条直线上。父节点升高一层级,祖父节点降低一层级。再交换父节点和祖父节点的颜色
if(parentDir == nodeDir){
//左子树,右旋
if(parentDir == 1){
grandfather->left = parent->right;
parent->right = grandfather;
//调整parent指针
Node *tmpRoot = grandfather->parent;
if(tmpRoot!=nullptr){
if(parent->value < tmpRoot->value)
tmpRoot->left = parent;
else
tmpRoot->right = parent;
} else {
std::cout << "parent[L] changed to root!\n";
root = parent;
}
parent->parent = tmpRoot;
grandfather->parent = parent;
if(grandfather->left != nullptr)
grandfather->left->parent = grandfather;
// std::cout << "left tree, right scroll\n";
// dump(tmpRoot);
}
//右子树,左旋
else{
grandfather->right = parent->left;
parent->left = grandfather;
//调整parent指针
Node *tmpRoot = grandfather->parent;
if(tmpRoot!=nullptr){
if(parent->value < tmpRoot->value)
tmpRoot->left = parent;
else
tmpRoot->right = parent;
}else{
std::cout << "parent[R] changed to root!\n";
root = parent;
}
parent->parent = tmpRoot;
grandfather->parent = parent;
if(grandfather->right != nullptr)
grandfather->right->parent = grandfather;
}
parent->color = BLACK;
grandfather->color = RED;
return;
}
//不在同一直线
//父为祖父左子树,当前节点为父右子树。
if(parentDir == 1){
//先将当前节点与父节点进行左旋,使得三节点在同一直线
grandfather->left = node;
parent->right = node->left;
node->left = parent;
//再进行一次左旋
grandfather->left = node->right;
node->left = grandfather;
//grandfather->left 由 node->right旋转而来
if(grandfather->left != nullptr)
grandfather->left->parent = grandfather;
//parent->right 同 node->left旋转而来
if(parent->right != nullptr)
parent->right->parent = parent;
//当前节点变成了祖父节点,父节点、祖父节点皆为当前节点的子节点
Node *tmpRoot = grandfather->parent;
if(tmpRoot!=nullptr){
if(node->value < tmpRoot->value)
tmpRoot->left = node;
else
tmpRoot->right = node;
} else {
root = node;
std::cout << "node[LR] changed to root!\n";
}
node->parent = tmpRoot;
parent->parent = node;
grandfather->parent = node;
}
else
{
grandfather->right = node;
parent->left = node->right;
node->right = parent;
grandfather->right = node->left;
node->left = grandfather;
if(grandfather->right!=nullptr)
grandfather->right->parent = grandfather;
if(parent->left!=nullptr)
parent->left->parent = parent;
//当前节点变成了祖父节点,父节点、祖父节点皆为当前节点的子节点
Node *tmpRoot = grandfather->parent;
if(tmpRoot!=nullptr){
if(node->value < tmpRoot->value)
tmpRoot->left = node;
else
tmpRoot->right = node;
} else {
root = node;
std::cout << "node[RL] changed to root!\n";
}
node->parent = tmpRoot;
parent->parent = node;
grandfather->parent = node;
}
//当前节点由红色变成黑色,祖父节点由黑色变成红色
node->color = BLACK;
grandfather->color = RED;
}
void dump(Node* node){
// std::cout << "dup...";
if(node == nullptr)
return;
if (node->left != nullptr)
dump(node->left);
std::string cl = (node->color == RED) ? "RED" : "BLACK";
std::cout << node->value << "[" << cl << "],";
if(node->right != nullptr)
dump(node->right);
}
Node *root = nullptr;
};
int main()
{
// std::cout << "hello";
RedBlackTree *tree = new RedBlackTree();
tree->addNode(15);
tree->addNode(13);
tree->addNode(11);
tree->addNode(12);
tree->addNode(8);
tree->addNode(1);
tree->addNode(10);
tree->addNode(6);
tree->addNode(9);
tree->dump();
return 0;
}