从二叉查找树到平衡二叉树再到红黑树

二叉查找树

定义:二叉查找树是一个二叉树结构,对于这个二叉树中的每一个节点X,它的左子树中的节点都小于X节点的关键字值,而右子树中的节点都大于X节点的关键字值。
二叉查找树也称作二叉排序树。

根据上面的定义,下面的一个二叉树就是一个二叉查找树:
二叉查找树

可以看到,按照中序遍历这个二叉树可以得到一个有序递增的序列。

定义二叉查找树的结构

与二叉树类似,其定义代码如下:

typedef struct BSNode {
   
    char ch;
    struct BSNode *left, *right;
} BSNode, *BSTree;

插入

构建二叉查找树的过程就是一个递归(或迭代)调用插入操作的处理过程,类似于二叉树的构建,但是二叉查找树的插入则需要先找到合适的插入位置,然后再插入节点。如上图所示的二叉查找树,可以按照这个序列来插入:6、4、8、3、5、7、9,首先插入节点6,此时这个节点是根节点,然后插入节点4,从树根开始遍历找到节点6的左孩子的位置,再插入节点,依此类推。
这个插入节点的函数如下:


void insert(BSTree *T, char c) {
   
    if(*T == NULL) {
   
        *T = createBSNode(c);
    } else {
   
        if((*T)->ch > c) {
   
            insert(&((*T)->left), c);
        } else if((*T)->ch < c) {
   
            insert(&((*T)->right), c);
        } else {
   
            return;
        }
    }
}
/*创建节点函数*/
BSNode* createBSNode(char c) {
   
    BSNode *node = malloc(sizeof(BSNode));
    node->ch = c;
    node->left = NULL;
    node->right = NULL;
    return node;
}

在上面的代码中每次插入一个节点就先与二叉查找树的根节点比较,如果根节点为NULL,说明这个二叉查找树为空树,那么就直接插入这个一个节点,如果要插入的节点元素比根节点的元素小,那么就在根节点的左子树中找到合适的插入位置,如果插入节点的元素比根节点的元素大,则在根节点的右子树中找到合适的插入位置,否则这个带插入的节点的元素已经存在于这个二叉查找树中,那么直接返回。

删除

二叉查找树的节点删除过程较插入稍微复杂一些,因为删除一个节点时,需要考虑三种情况:

  1. 待删除的节点为叶子节点;
  2. 待删除的节点有一个子树为空;
  3. 待删除的节点的左右子树均不为空;

以下图为例:


对于情况1,只需要从二叉查找树中直接删除这个节点,调整其父节点的指针为NULL,如图中的节点2,6,9这三个节点,如果要删除节点2,直接将节点3的左孩子指针设置为NULL,然后删除节点2,对于情况2,只需要调整将待删除节点的父节点的指针,指向待删除节点的不为空的子树根节点,如图中的节点1,7, 3, 5,如果要删除节点3,直接将节点1的右孩子指针指向节点2,然后删除节点3,对于情况3,要保证删除节点之后,不改变二叉查找树的结构,所以删除节点后,树中元素的相对位置不能改变,如节点4和8,可以有以下三种方式,假设待删除节点为p:

  1. 使用p的左孩子代替p,p的右子树称为p的左子树的最右节点的右子树,如果要删除节点4,那么将节点8的左孩子指针指向节点1,然后节点7代表的子树成为节点3的右子树,这个结果所代表的树也为二叉查找树;
  2. 使用p的中序遍历的直接前驱代替p,然后删除p的直接前驱节点,如果要删除节点4,可以使用节点4的中序遍历的直接前驱节点3代替节点4,然后删除节点3;
  3. 使用p的中序遍历的直接后继代替p,然后删除p的直接后继节点,如果要删除节点4,可以使用节点4的中序遍历的直接后继节点5,代替节点4,然后删除节点5;

对于方式2和方式3,因为p的中序遍历的直接前驱或者直接后继节点至少有一个子树为空,所以使用p的中序遍历的直接前驱或者直接后继替换后,又将这个删除问题转化成上面3中情况中的情况1或者情况2。

基于上面的分析,删除节点的代码如下:

//删除节点
void deleteNode(BSNode *pre, BSNode *p) {
   
    if(pre->left == p) {
   
        pre->left = p->left != NULL ? p->left : p->right;
    } else {
   
        pre->right = p->left != NULL ? p->left : p->right;
    }
}

void delete(BSTree *T
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值