红黑树

  红黑树的性质:

                         1.每个结点或是红色的,或是黑色的

                         2.根结点都是黑色的

                         3.如果一个结点是红色的,则它的两个子结点都是黑色的。

                         4.对于每个结点,从该节点到其所有后代的NULL的简单路径上,均包含相同数目的黑色结点

红黑树的插入:

          对于红黑树T,将新结点z插到T中,就像普通的二叉查找树样,先把z插入到树中,然后再把z着为红色(因为若把该节点涂成黑色,则将会建立一条更长的黑结点路径)。若z的父结点为黑色,则不会破坏红黑树性质,完成插入;若父结点为红色,则破坏红黑树的性质。当T为空,那么在就是跟结点,违反了性质2;若T不为空,违反性质3。

红黑树的插入分下面几种情况(T为空时,就不列出来,因为直接把z结点作为根结点,并且把z涂成黑色即可)圆圈表示黑色,正方形表示红色:

一、z结点的父结点是z祖父的左儿子(其中的z指向新插入的结点k3)

(一)z的叔结点是空

1.  z是左儿子

                                                       

2.  z是右儿子

                                                    

(二) z的叔结点不为空

1.  z的叔结点为红色,如下图通过变换,将z的指针上移,指向k1。然后以k1为新的z进入下次循环,调整红黑树,使得树满足红黑树的性质

                                                   

2.  z的叔结点为黑色,其中分为,z为左儿子和右儿子,其变换图下图。当z为右儿子时,z指向k2,通过变换转化为z的结点为左儿子的情况。

                                           

对于上述的插入,当为(一)中的两种情况,通过变换后,满足红黑树的性质。在编程中通过设置flag标示来结束循环;当为(二)中2的第一种情况(即z为左儿子),通过变换后,满足红黑树的性质,通过判断z的父结点是否为黑色来结束循环。其他情况中若z指向根结点,则把z的颜色涂成黑色,结束循环。

当z的父结点为z祖父结点的右儿子时,可以类似z父结点为左儿子的进行类似的变换,使得满足红黑树的性质。

红黑树的删除

     红黑树的删除比红黑树的插入复杂的多。首先由二叉查找树删除的性质可知,删除的结点z一般为:z无儿子、z仅有一个左儿子、z仅有一个右儿子。因为当删除的结点有两个儿子时,总可以通过查找该结点的后继z,后继中的元素element替换该节点中的元素element。这样就把删除转化为上述的三种中的某一种情况。

下面考虑删除中的几种情况:删除结点为z

一、z为红色时

      这个比较简单,由上分析可知z最多只有一个儿子,因为z为红色,要有儿子必为两个黑色儿子,这与上面分析矛盾,所以z为红色时无儿子。删除时直接删除即可,不会破坏红黑树的性质。

二、z为黑色时,且z有一个儿子或者z为根节点无儿子。

    当z有一个儿子时,该结点必为红色,若为黑色,由于z是只有一个儿子的,其不满足红黑树的性质4。删除方法:直接把z儿子中的元素element代替z中的元素element,然后把z儿子结点删除;若z为根结点直接删除即可。

三、z为黑色且z无儿子(z指向的k2为要删除的结点,通过把k2的颜色变为红色然后删除,这样不会破坏红黑树的性质)

1.  z的兄弟结点为红色

    为了满足红黑树性质4,则其兄弟结点必有两个儿子且都为黑色。进行下面图中的调整

                                               

变换后,将k1涂成黑色,k2和A涂成红色,然后删除k2。

 

2.  z的兄弟结点为黑色且z的父结点为黑,且兄弟结点有两个儿子结点(为了满足红黑树的性质4,则兄弟结点的儿子必为红色)

                                                 

通过图中的变换后,其转化为1的情况。

3.  z的兄弟结点为黑且z的父结点为黑,兄弟结点有一个右儿子

                                               

直接删除k2的结点。

4. z的兄弟结点为黑且z的父结点为黑,兄弟结点有一个左儿子

                                        

通过图中的变换,变为情况3

5.  z的兄弟结点为黑且z的父结点为黑,兄弟结点无儿子,做法是:把z和z的兄弟结点涂成红色,z的父亲结点x为双黑色,然后删除z结点。接下来就是怎样把x的双黑去掉一层。此情况比较复杂,下面会做单独讨论的。

                                              

6.  z的父结点为红(则z兄弟结点必为黑)且z的兄弟结点有两个儿子(其儿子必为红色,可以分析红黑树的性质得到)

                                          

通过变换后,删除k2结点

7. z的父结点为红(则z兄弟结点必为黑)且z的兄弟结点有一个左儿子(其儿子必为红色,可以分析红黑树的性质得到)

                                       

变为情况8

8.  z的父结点为红(则z兄弟结点必为黑)且z的兄弟结点有一个右儿子(其儿子必为红色,可以分析红黑树的性质得到)

                                     

通过变换后,直接删除k2

9.  z的父结点为红(则z兄弟结点必为黑)且z的兄弟结点无儿子

                                   

通过变换后直接删除k2。

上面分析了删除的结点k2为左儿子的情况,当删除结点为右儿子可以类比的分析。下面给出上面情况5的分析:

对于删除结点k2、k2的父亲、k2的兄弟都为黑色,为了不破坏红黑树的性质,我们可以把k2、k2的兄弟涂成红色,把k2的父亲假设为双黑色,便可以把k2结点删除,这样并未破坏红黑树的性质。令x指向z的双黑父亲结点,下面的主要任务是把x指向的结点去掉一层黑色,怎么才能去掉一层黑色呢?分为下面几种情况

下面以x是左儿子为例说明。

1. x的兄弟结点为红色

      x为双黑色,为了满足红黑树性质4,x的兄弟结点后代必有黑色结点。

                                    

通过上面的变换后,其变为下述的情况2、3、4中的某一种,然后继续循环,知道满足红黑树的性质。

2.  x的兄弟结点为黑色,且兄弟结点有两个儿子也都是黑色的

                                                 

     第一种直接把双黑x指向的A结点去除一层黑色,同时没有破坏红黑树的性质,完成任务;第二种x指向的A结点去除一层黑色,但是B结点变为双黑的,x指针上移指向B结点,进入下次循环,把B结点的黑色去除一层。

3.  x的兄弟结点为黑色,且兄弟结点的左儿子为红色,右儿子为黑色(其中B的三角形表示该结点颜色可为黑、红任意一种)

                                                        

通过上面的变换,转化为情况4。进入下次循环。

4.  x的兄弟结点为黑色,右儿子为红色(其中B、C结点的三角形表示该结点可以为黑、红中任意一种颜色)

                                                   

通过上变化后,A结点的黑色去掉一层,因为在原始图中由A的父结点到A的两个儿子黑色结点数为2(因为A为双黑的),变换后由D到A的两个儿子黑色结点数也为2(此时A只是一层黑色)。

代码如下:

rb-tree.h

#pragma once
#include<iostream>
#include<string>
using namespace std;
template<typename Comparable>
struct Node
{
	Comparable element;//元素
	Node* left; //指向左儿子的左指针
	Node* right;//指向右儿子的右指针
	Node* parent; //指向父亲结点
	string color; //该结点的颜色
	Node(Comparable x,Node* l,Node* r,Node* p,string c):element(x),left(l),right(r),parent(p),color(c){}
};
enum MODE
{
	PRE,
	MID,
	POST
};

template<typename Comparable>
class rbtree
{
public:
	rbtree();
	rbtree(rbtree &rh);
	Comparable findmin();
	Comparable findmax();
	void insert(Comparable x);
	void remove(Comparable x);
	void print(MODE mode);
private:
	Node<Comparable>* root;
	Node<Comparable>* findmin(Node<Comparable>* t);//寻找最小
	Node<Comparable>* findmax(Node<Comparable>* t);//寻找最大
	void insert(Node<Comparable>* &t);//插入
	Node<Comparable>* remove(Comparable x,Node<Comparable>* &t);//删除,其中t为要删除的结点
	void Lremove(Node<Comparable>* &x);
	void Rremove(Node<Comparable>* &x);
	void leftrotate(Node<Comparable>* t);//左旋
	void rightrotate(Node<Comparable>* t);//右旋
	void PrePrint(Node<Comparable>* t);
	void MidPrint(Node<Comparable>* t);
	void PostPrint(Node<Comparable>* t);
};


 

rb-tree.cpp

#include "stdafx.h"
#include"rb-tree.h"
#include<iostream>
#include<string>
using namespace std;
template<typename Comparable>
rbtree<Comparable>::rbtree()
{
	root=NULL;
}

template<typename Comparable>
rbtree<Comparable>::rbtree(rbtree& rh)
{
	root=rh.root;
}

template<typename Comparable>
Comparable rbtree<Comparable>::findmin()
{
	return findmin(root)->element;
}
template<typename Comparable>
Node<Comparable>* rbtree<Comparable>::findmin(Node<Comparable>* t)
{
	if(t==NULL)
		return NULL;
	else if(t->left==NULL)
		return t;
	else
		findmin(t->left);
}

template<typename Comparable>
Comparable rbtree<Comparable>::findmax()
{
	return findmax(root)->element;
}
template<typename Comparable>
Node<Comparable>* rbtree<Comparable>::findmax(Node<Comparable>* t)
{
	if(t==NUUL)
		return NULL;
	else if(t->left==NULL)
		return t;
	else
		findmax(t->right);
}

template<typename Comparable>
void rbtree<Comparable>::insert(Comparable x)
{
	Node<Comparable>* t=new Node<Comparable>(x,NULL,NULL,NULL,"RED");
	insert(t);
}
template<typename Comparable>
void rbtree<Comparable>::insert(Node<Comparable>* &z)//插入
{
	Node<Comparable>* x=root;
	Node<Comparable>* y=NULL;
	while(x!=NULL)
	{
		y=x;
		if(z->element<x->element)
			x=x->left;
		else
			x=x->right;
	}
	z->parent=y;
	if(y==NULL)
		root=z;
	else if(z->element<y->element)
		y->left=z;
	else
		y->right=z;
	//调整红黑树,使其满足红黑树的性质
	bool flag=true;
	while(flag&&(z!=root)&&(z->parent->color=="RED"))//z的父结点是红色的且,z不是根结点
	{
		if(z->parent==z->parent->parent->left)//z的父亲结点是祖父的左儿子时
		{
			if(z->parent->parent->right==NULL)//如果其叔结点为空
			{
				if(z==z->parent->left)
				{
					z->parent->color="BLACK";
					z->parent->parent->color="RED";
					rightrotate(z->parent->parent);
					flag=false;
				}
				else
				{
					z->color="BLACK";
					z->parent->parent->color="RED";
					leftrotate(z->parent);
					rightrotate(z->parent);
					flag=false;
				}

			}
			else
			{
			y=z->parent->parent->right;  //是z结点的叔结点
			if(y->color=="RED")          //case 1: z的叔结点是红色的
			{
				//cout<<"0"<<endl;
				z->parent->color="BLACK";
				y->color="BLACK";
				z->parent->parent->color="RED";
				z=z->parent->parent;   // z指针上移
			}
			else if(z==z->parent->right)  //case 2: z是右儿子
			{
				//cout<<"1"<<endl;
				z=z->parent;
				leftrotate(z);
			}
			else
			{
			//	cout<<"2"<<endl;
			 z->parent->color="BLACK";  //case 3:
			 z->parent->parent->color="RED";
			 rightrotate(z->parent->parent);
			}
			}
		}
		else                           //z的父亲结点是祖父的右儿子
		{
			if(z->parent->parent->left==NULL)//叔结点为空
			{
				if(z==z->parent->right)
				{
					z->parent->color="BLACK";
					z->parent->parent->color="RED";
					leftrotate(z->parent->parent);
					flag=false;
				}
				else
				{
					z->color="BLACK";
					z->parent->parent->color="RED";
					rightrotate(z->parent);
					leftrotate(z->parent);
					flag=false;
				}
			}
			else                 
			{
			y=z->parent->parent->left;//是z结点的叔结点
			if(y->color=="RED")          //case 1
			{
			//	cout<<"3"<<endl;
				z->parent->color="BLACK";
				y->color="BLACK";
				z->parent->parent->color="RED";
				z=z->parent->parent;   // z指针上移
			}
			else if(z==z->parent->right) //case 2
			{
			//	cout<<"4"<<endl;
				z->parent->color="BLACK";
				z->parent->parent->color="RED";
				leftrotate(z->parent->parent);
			}
			else   //case 3:
			{
				z=z->parent;
			   rightrotate(z);
		    }  
			}
		}
	}
	root->color="BLACK";
}

template<typename Comparable>
void rbtree<Comparable>::remove(Comparable x)
{
	Node<Comparable>* z=remove(x,root);
	if(z->color=="RED")  //删除的结点为红色,则直接删除,不会破坏红黑树的性质
	{                   //因为删除的结点最多只有一个儿子结点,若删除结点为红色结点,则其必没有儿子结点。
		if(z==z->parent->left)
			z->parent->left=NULL;
		else
			z->parent->right=NULL;
		delete z;
	}
	else            //删除结点为黑色,删除会破坏红黑树的性质     
	{
		if((z==root)||(z->left!=NULL)||(z->right!=NULL)) //z为根结点,(或者z有一个儿子,则该儿子必为红色的)
		{
			Node<Comparable>* temp;
			if(z->left!=NULL)
			{
				z->element=z->left->element;
				temp=z->left;
				z->left=NULL;
				delete temp;
			}
			else if(z->right!=NULL)
			{
				z->element=z->right->element;
				temp=z->right;
				z->right=NULL;
				delete temp;
			}
			else
			   delete z;	
		}
		else //z不为根结点,并且z无儿子结点
		{
			Node<Comparable>* temp=z;
			Node<Comparable>* w=NULL;  //记录z的兄弟结点,因为z结点为黑色,所以其必有兄弟结点。
			bool flag=true;            //标记是否把结点删除
			while(flag&&z!=root&&z->color=="BLACK")//并且若z有儿子也为红色的
			{
				if(z==z->parent->left)//z为左儿子结点时
				{
					w=z->parent->right;//z的右兄弟
					if(w->color=="RED")  //case1:z的兄弟为红色
					{
						w->color="BLACK";
						z->parent->color="RED";
						leftrotate(z->parent);
						w=z->parent->right;//左旋后,z的兄弟结点改变
						if(w->left==NULL&&w->right==NULL)//w无子结点,直接改变某些结点颜色,然后删除,
						{                                //否则进入下次循环
							z->color="RED";
							w->color="RED";
							z->parent->color="BLACK";
							z->parent->left=NULL;
							delete z;
							flag=false;
						}
					}
					 else if(w->left!=NULL&&w->right!=NULL)//case 2:兄弟结点两个儿子都存在,则其必为红色
					 {
						if(z->parent->color=="BLACK")//父结点为黑色
						{
							w->left->color="BLACK";
						    w->right->color="BLACK";
							w->color="RED";         //进入下次循环,变成情况1
						}
						else                   //父结点为红色
						{
						   z->parent->color="BLACK"; 
						   z->color="RED";
						   w->right->color="BLACK";
						   w->color="RED";
						   leftrotate(z->parent);
						   z->parent->left=NULL;
						   delete z;
						   flag=false;
						}
					 }
				    else if(w->left!=NULL)  //case 3:左儿子不为空,必为红色,右儿子为空
					{
						  w->color="RED";
						  w->left->color="BLACK";
						  rightrotate(w);   //进入下次循环,变成情况4
					}
				   else if(w->right!=NULL)//case 4:兄弟结点右儿子不为空,必为红色,左儿子为空
					{
						 z->color="RED";
						 if(z->parent->color=="BLACK")//父结点是黑色,要将z的右兄弟儿子变为黑色
							 w->right->color="BLACK";
						leftrotate(z->parent);
						z->parent->left=NULL;
						delete z;
						flag=false;
					}
				  else                //case 5:兄弟结点的两个儿子都为空
					{
						if(z->parent->color=="BLACK")//父结点为黑色        
						{
							Node<Comparable>* x=z->parent;
							z->color="RED";
							w->color="RED";
							x->left=NULL;
							delete z;
							if(x!=root)
							{
								if(x==x->parent->left)
								Lremove(x);
							else
								Rremove(x);
								flag=false;
							}
							else
							{
								flag=false;
							}
						}
						else                     //父结点为红色
						{
							z->color="RED";
							w->color="RED";
							z->parent->color="BLACK";
							z->parent->left=NULL;
							delete z;
							flag=false;
						}

					}
						
				}
			else        //z为右儿子结点时
			{
				w=z->parent->left;//z的左兄弟结点
				if(w->color=="RED")          //case 1:
				{
					z->parent->color="RED";
					w->color="BLACK";
					rightrotate(z->parent);
					w=z->parent->left;
					if(w->left==NULL&&w->right==NULL)
					{
						z->color="RED";
						w->color="RED";
						z->parent->color="BLACK";
						z->parent->right=NULL;
						delete z;
						flag=false;
					}
				}
				else if(w->left!=NULL&&w->right!=NULL)//case 2:
				{
					if(z->parent->color=="BLACK")
					{
						w->left->color="BLACK";
						w->right->color="BLACK";
						w->color="RED";
					}
					else
					{
						z->parent->color="BLACK";
						w->color="RED";
						z->color="RED";
						w->left->color="BLACK";
						rightrotate(z->parent);
						z->parent->right=NULL;
						delete z;
						flag=false;
					}
				}
				else if(w->left!=NULL)   //case 3:
				{
					z->color="RED";
					if(z->parent->color=="BLACK")//父结点是黑色
						w->left->color="BLACK";
					rightrotate(z->parent);
					z->parent->right=NULL;
					delete z;
					flag=false;
				}
				else if(w->right!=NULL)   //case 4:
				{
					w->color="RED";
					w->right->color="BLACK";
					leftrotate(w);
				}
				else                  //case 5:
				{
					if(z->parent->color=="BLACK")
					{
						Node<Comparable>* x=z->parent;
						z->color="RED";
						w->color="RED";
						x->right=NULL;
						delete z;
						if(x!=NULL)
						{
							if(x==x->parent->left)
							Lremove(x);
						else
							Rremove(x);
							flag=false;
						}
						else
						{
							flag=false;
						}
						
					}
					else
					{
						w->color="RED";
						z->color="RED";
						z->parent->color="BLACK";
						z->parent->right=NULL;
						delete z;
						flag=false;
					}
				}
			}
		  }

		}
	}
}

template<typename Comparable>
Node<Comparable>* rbtree<Comparable>::remove(Comparable x,Node<Comparable>* &t)//返回要删除的结点的指针
{
	if(t==NULL)
		return NULL;
	else if(x<t->element)
		remove(x,t->left);
	else if(x>t->element)
		remove(x,t->right);
	else if(t->left!=NULL&&t->right!=NULL)//删除结点左右指针都不是空,返回右侧中最小的元素指针
	{
		Node<Comparable>* temp=findmin(t->right);
		t->element=temp->element;
		return temp;
	}
	else    //左右指针都为空或只有一个为空。
		return t;
}

template<typename Comparable>
void rbtree<Comparable>::Lremove(Node<Comparable>* &x)//父结点是黑色且是左儿子,且兄弟结点无儿子的删除
{
	bool flag=true;
	Node<Comparable>* w=x->parent->right;//兄弟结点
	while(flag&&x!=root)
	{
		if(w->color=="RED")//case 1:兄弟结点为红色
		{
			x->parent->color="RED";
			w->color="BLACK";
			leftrotate(x->parent);
			w=x->parent->right;
		}
		else if(w->left->color=="BLACK"&&w->right->color=="BLACK")//case 2:兄弟结点为黑色,
		{                                                         //且兄弟结点两个儿子为黑色
			if(x->parent->color=="RED")//父结点为红色
			{
				x->parent->color="BLACK";
				w->color="RED";
				flag=false;
			}
			else                  //父结点为黑色
			{
				if(x->parent==root)
				{
					w->color="RED";
					flag=false;
				}
				else
				{
				w->color="RED";
				x=x->parent;   //此时x指针指向父结点,表明父结点变为双黑
				w=x->parent->right;
				}
			}
		}
		else if(w->right->color=="RED")  //case 3:兄弟结点右儿子是红色,左儿子是任意
		{
			w->color=x->parent->color;
			x->parent->color="BLACK";
			w->right->color="BLACK";
			leftrotate(x->parent);
			flag=false;
		}
		else                    //case 4:兄弟结点左儿子是红色,右儿子是黑色
		{
			w->color="RED";
			w->left->color="BLACK";
			rightrotate(w);              //变成情况3
			w=x->parent->right;
		}
	}                                                             

}
template<typename Comparable>
void rbtree<Comparable>::Rremove(Node<Comparable>* &x)//父结点是黑色且是右儿子,且兄弟结点无儿子的删除
{
	bool flag=true;
	Node<Comparable>* w=x->parent->left;
	while(flag&&x!=root)
	{
		if(w->color=="RED")//case 1:兄弟结点为红
		{
			x->parent->color="RED";
			w->color="BLACK";
			rightrotate(x->parent);
			w=x->parent->left;
		}
		else if(w->left->color=="BLACK"&&w->right->color=="BLACK")//case 2:兄弟结点两个儿子为黑
		{
			if(x->parent->color=="RED")
			{
				w->color="RED";
				x->parent->color="BLACK";
				flag=false;
			}
			else
			{
				if(x->parent==root)
				{
					w->color="RED";
					flag=false;
				}
				else
				{
					w->color="RED";
					x=x->parent;
					w=x->parent->left;
				}
			}
		}
		else if(w->left->color=="RED") //case 3:兄弟结点左儿子为红,右儿子为任意
		{
			w->color=x->parent->color;
			x->parent->color="BLACK";
			w->left->color="BLACK";
			rightrotate(x->parent);
			flag=false;
		}
		else   //case 4:右儿子为红,左儿子为黑。变化后变为
		{
			w->color="RED";
			w->right->color="BLACK";
			leftrotate(w);
			w=x->parent->left;
		}
	}
}

template<typename Comparable>
void rbtree<Comparable>::print(MODE mode)
{
	if(mode==PRE)
		PrePrint(root);
	else if(mode==MID)
		MidPrint(root);
	else if(mode==POST)
		PostPrint(root);
	else
		return;
	cout<<endl;
}
template<typename Comparable>
void rbtree<Comparable>::PrePrint(Node<Comparable>* t)
{
	if(t==NULL)
		return;
	else
	{
	cout<<"("<<t->element<<","<<t->color<<")"<<" ";
	PrePrint(t->left);
	PrePrint(t->right);
	}
}
template<typename Comparable>
void rbtree<Comparable>::MidPrint(Node<Comparable>* t)
{
	if(t==NULL)
		return;
	else
	{
	MidPrint(t->left);
	cout<<"("<<t->element<<","<<t->color<<")"<<" ";
	MidPrint(t->right);
	}
}
template<typename Comparable>
void rbtree<Comparable>::PostPrint(Node<Comparable>* t)
{
	if(t==NULL)
		return;
	else
	{
	PostPrint(t->left);
	PostPrint(t->right);
   cout<<"("<<t->element<<","<<t->color<<")"<<" ";
	}
}

template<typename Comparable>
void rbtree<Comparable>::leftrotate(Node<Comparable>* t)//左旋
{
	Node<Comparable>* temp=t->right;
	t->right=temp->left;
	temp->left=t;
	if(t!=root)
	{
		temp->parent=t->parent;
		if(t==t->parent->left)
			t->parent->left=temp;
		else
			t->parent->right=temp;
		
	}
	else
		root=temp;
	t->parent=temp;
	if(t->right!=NULL)
		t->right->parent=t;

}

template<typename Comparable>
void rbtree<Comparable>::rightrotate(Node<Comparable>* t) //右旋
{
	Node<Comparable>* temp=t->left;
	t->left=temp->right;
	temp->right=t;
	if(t!=root)
	{
		temp->parent=t->parent;
		if(t==t->parent->right)
			t->parent->right=temp;
		else
			t->parent->left=temp;
		
		
	}
	else
		root=temp;
	t->parent=temp;
	if(t->left!=NULL)
		t->left->parent=t;
}


RB_Tree.cpp

// RB_Tree.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include"rb-tree.h"
#include"rb-tree.cpp"
#include<iostream>
#include<string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
	rbtree<int> r;
	int a[9]={18,10,20,9,15,19,21,14,16};
	for(int i=0;i<9;i++)
		r.insert(a[i]);
	r.remove(21);
	r.print(MID);
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值