本人看的是《算法》这本书,所以红黑树的理解是一下这样的:
个人认为这样比结点标红黑更好理解。很好下面是红黑树的定义:
1、红黑树其实是2-3结点树的二叉平衡表示形式。
2、红链接均为左连接(左倾红黑树)
3、没有任何结点同时连接着两条红链
4、红黑树是完美平衡的,也就是任何空连接(NULL)到根节点经过的黑色分支数量是相等的
其中2、3都是为了维持是2-3树的二叉平衡形式,4、是为了对树高进行一个很好的限制,因为我们知道二叉搜索树的时间复杂度是由树高决定的,所以如果一棵二叉树越平衡那么搜索的性能就约好。
为什么要引进红黑树?直接2-3树不是更好?
这里我们要知道一个问题,2-3树的插入和删除的分类情况是非常多的,而且插入和删除后为了维持树的绝对平衡性,会带来许多的操作,这样就会对树的运行效率带来极大的挑战,也许此时额外的负担已经超过维持平衡后所带来的收益了。而我们反观红黑树,单从树的角度来看并不是完美平衡的,但是我们有条件4的限制,那么会出现一个有趣的效果,那就是最长的结点高度不会高于任意结点高度的两倍,并且红黑树的插入和删除情况少,维护的代价低。那么我们可以看出下面一个收益:
TotalCost = BalanceCost + SearchCost + InsertCost + DeleteCost。所以我们可以把红黑树看成是一种2-3树的变体(在插入,删除过多的情况下),也就是在某些情况下TotalCost会更加小的特殊情况。
下面是对红黑树的实现:
数据结构:
#define Red true;
#define Black false;
typedef struct treeNode{
KeyType Key;
ValType val;
struct treeNode *left;
struct treeNode *right;
bool color;
}RBTree,*PRBTree;
1、插入:
//插入函数
PRBTree insertKey(PRBTree root,char key,int val)
{
//空就生成新结点
if(!root)
{
CreateNode(&root,key,val);
return root;
}
if(root->Key > key)
root->left = insertKey(root->left,key,val);
else if(root->Key < key)
root->right = insertKey(root->right,key,val);
else
root->val = val;
if(isRed(root->left) && isRed(root->right))
colorFlip(root);
if(isRed(root->left) && isRed(root->left->left))
{
root = rightRotate(root);
colorFlip(root);
}
if(isRed(root->right))
root = leftRotate(root);
return root;
}
2、删除函数
//删除任意结点
void deleteNode(PRBTree *root,char key)
{
//----------------------------------策略是利用删除最大和最小函数-------------------------------------
PRBTree T;
if(!(*root))
return;
//在左子树
if((*root)->Key > key)
{
if(isRed((*root)->left) || isRed((*root)->left->left));
else if(!isRed((*root)->left) && !isRed((*root)->right))
borrowKeyMin(root);
deleteNode(&(*root)->left,key);
}
else if((*root)->Key < key)
{
if(isRed((*root)->left))
*root = rightRotate(*root);
else if((*root)->right)
{
if(isRed((*root)->right->left))
(*root)->right = rightRotate((*root)->right);
else if(!isRed((*root)->left) && !isRed((*root)->right))
borrowKeyMax(root);
}
deleteNode(&(*root)->right,key);
}
else
{
//存在左子树,那么用左子树的最大值来代替并且删除
if((*root)->left)
{
PRBTree T = DeleteMax(&(*root)->left);
(*root)->Key = T->Key;
(*root)->val = T->val;
}
//存在右子树,用右子树的最小值代替并且删除
else if((*root)->right)
{
T = DeleteMin(&(*root)->right);
(*root)->Key = T->Key;
(*root)->val = T->val;
}
//叶子结点,因为前面每一步都是经过变换的,所以可以直接删除
else
(*root) = NULL;
}
*root = fixUp(*root);
}
3、插入删除的辅助函数
//判断结点的颜色
bool isRed(PRBTree Node);
//颜色反转函数
void colorFlip(PRBTree Node);
//插入函数
PRBTree insertKey(PRBTree root,char key,int val);
//插入函数,需要刷新一下根节点
PRBTree insert(PRBTree root,char key,int val);
//生成一个新的结点
void CreateNode(char key,int val);
//右旋转
PRBTree rightRotate(PRBTree Node);
//左旋转
PRBTree leftRotate(PRBTree Node);
//展示红黑树
void Display(PRBTree root);
//利用插入函数直接创建一个红黑树
PRBTree CreateRBTree(char *key,int *val,int keySize);
//删除最小键
PRBTree deleteMin(PRBTree *root);
//删除最小键的直接调用函数,需要重置根
PRBTree DeleteMin(PRBTree *root);
//删除最大键
PRBTree deleteMax(PRBTree *root);
//删除最大键的直接调用函数,需要重置根
PRBTree DeleteMax(PRBTree *root);
//借键的情况Max
void borrowKeyMax(PRBTree *Node);
//借键的情况Min
void borrowKeyMin(PRBTree *Node);
//修复红黑树
PRBTree fixUp(PRBTree root);
//删除任意结点
void deleteNode(PRBTree *root,char key);
//删除任意结点,直接调用函数
PRBTree DeleteNode(PRBTree *root,char key);
//查找函数
bool SearchKey(PRBTree root,char key,PRBTree *find);
4、关键的辅助函数的实现:
//颜色反转函数
void colorFlip(PRBTree Node)
{
if(isRed(Node))
{
Node->color = Black;
}
else
Node->color = Red;
if(Node->left)
{
if(isRed(Node->left))
{
Node->left->color = Black;
}
else
Node->left->color = Red;
}
if(Node->right)
{
if(isRed(Node->right))
{
Node->right->color = Black;
}
else
Node->right->color = Red;
}
}
//右旋转
PRBTree rightRotate(PRBTree Node)
{
PRBTree Temp = Node->left;
Node->left = Temp->right;
Temp->right = Node;
Temp->color = Node->color;
Node->color = Red;
return Temp;
}
//左旋转
PRBTree leftRotate(PRBTree Node)
{
PRBTree Temp = Node->right;
Node->right = Temp->left;
Temp->left = Node;
Temp->color = Node->color;
Node->color = Red;
return Temp;
}
//修复红黑树
PRBTree fixUp(PRBTree root)
{
if(!root)
return NULL;
if(isRed(root->right) && !isRed(root->left))
root = leftRotate(root);
if(isRed(root->left) && isRed(root->left->left))
root = rightRotate(root);
if(isRed(root->left) && isRed(root->right))
colorFlip(root);
return root;
}
//借键的情况
void borrowKeyMin(PRBTree *Node)
{
colorFlip(*Node);
if((*Node)->right)
{
if(isRed((*Node)->right->left))
{
(*Node)->right = rightRotate((*Node)->right);
(*Node) = leftRotate((*Node));
colorFlip((*Node));
}
}
}
//借键的情况Max
void borrowKeyMax(PRBTree *Node)
{
colorFlip(*Node);
if((*Node)->left)
{
if(isRed((*Node)->left->left))
{
(*Node) = rightRotate((*Node));
colorFlip((*Node));
}
}
}
//删除最小键
PRBTree deleteMin(PRBTree *root)
{
//-------------------------------目的是确保需要删除的结点的上一个节点不是2结点,这样就可以直接删除--------------------------------
//可以删除的结点
if(!(*root))
return NULL;
if(isRed(*root) && !(*root)->left)
{
PRBTree Temp = *root;
*root = (*root)->right;
return Temp;
}
//只剩最后一个结点的情况
if(!(*root)->left && !(*root)->right)
{
PRBTree Temp = *root;
*root = NULL;
return Temp;
}
//需要借键的情况
if(!isRed((*root)->left) && !isRed((*root)->right))
borrowKeyMin(root);
PRBTree Temp = deleteMin(&(*root)->left);
(*root) = fixUp(*root);
return Temp;
}
//删除最大键
PRBTree deleteMax(PRBTree *root)
{
//-------------------------------目的是确保需要删除的结点的上一个节点不是2结点,这样就可以直接删除--------------------------------
//将原本就是3结点的结点旋转成符合删除最大的形式
if(!(*root))
return NULL;
if(isRed((*root)->left))
*root = rightRotate(*root);
else if((*root)->right)
{
if(isRed((*root)->right->left))
(*root)->right = rightRotate((*root)->right);
}
//可以删除的结点
if(isRed((*root)) && !(*root)->right)
{
PRBTree Temp = (*root);
(*root) = (*root)->left;
return Temp;
}
//只剩最后一个结点的情况
if(!(*root)->left && !(*root)->right)
{
PRBTree Temp = *root;
*root = NULL;
return Temp;
}
//需要借键的情况
if(!isRed((*root)->left) && !isRed((*root)->right))
borrowKeyMax(root);
PRBTree Temp = deleteMax(&(*root)->right);
(*root) = fixUp(*root);
return Temp;
}
想看思路的可以看这里,很清楚:
https://www.jianshu.com/p/37c845a5add6