红黑二叉树

一、二叉查找树

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值