查找------二叉排序树

《大话数据结构》

二叉排序树

二叉排序树(Binary Sort Tree),又称为二叉查找树。它或者是一棵空树,或者是具有下列性质的二叉树。
■若它的左子树不空,则左子树上所有结点的值均小于它的根结构的值;·
■若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
■它的左、右子树也分别为二叉排序树。
构造一棵二叉排序树的目的,其实并不是为了排序,而是为了提高查找插入删除关键字的速度。不管怎么说,在一个有序数据集上的查找,速度总是要快于无序的数据集的,而二叉排序树这种非线性的结构,也有利于插入和删除的实现。

查找

/*递归查找二叉排序树T中是否存在key,*/
/*指针f指向T的双亲,其初始调用值为NULL*/
/*若查找成功,则指针p指向该数据元素结点,并返回TRUE*/ /*否则指针p指向查找路径上访问的最后一个结点并返回FALSE*/
typedef struct BiTNode {
  int data;
  struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

Status SearchBST(BiTree T, int key, BiTree f, BiTree *p) {
  if (!T) /*查找不成功*/
  {
    *p = f;
    return FALSE;
  }

  else if (key == T->data) /*查找成功*/
  {
    *p = T;
    return TRUE;
  }

  else if (key < T->data)
    return SearchBST(T->lchild, key, T, p); /*在左子树继续查找*/
  else
    return SearchBST(T->rchild, key, T, p); /*在右子树继续查找*/
}

插入

Status InsertBST(BiTree *T, int key) {
  BiTree p, s;
  if (!SearchBST(*T, key, NULL, &p)) /*查找不成功 */
  {
    s = (BiTree)malloc(sizeof(BiTNode));
    s->data = key;
    s->lchild = s->rchild = NULL;
    if (!p)
      *T = s; /*插入s为新的根结点*/
    else if (key < p->data)
      p->lchild = s; /*插入s为左孩子*/
    else
      p->rchild = s; /*插入s为右孩子 */
    return TRUE;
  } 
  else
    return FALSE;
  /*树中已有关键字相同的结点,不再插入*/
}

删除
对于要删除的结点只有左子树或只有右子树的情况,相对也比较好解决。那就是结点删除后,将它的左子树或右子树整个移动到删除结点的位置即可,可以理解为独子继承父业。比如图8-6-9,就是先删除35和99结点,再删除58结点的变化图,
在这里插入图片描述
但是对于要删除的结点既有左子树又有右子树的情况怎么办呢?比如图8-6-10中的47结点若要删除了,它的两儿子以及子孙们怎么办呢?
在这里插入图片描述
若直接删除47节点,然后把子树的节点重新插入,这么做效率很低且不说,还会导致整个二叉排序树结构发生很大的变化,有可能会增加树的高度。
因此,比较好的办法就是,找到需要删除的结点p的直接前驱(或直接后继)s,用s来替换结点p,然后再删除此结点s,如图8-6-12所示。
在这里插入图片描述
在这里插入图片描述

平衡二叉数

平衡二叉树(Self-Balancing Binary Search Tree或 Height-Balanced Binary SearchTree),是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1

不平衡的最坏情况就是像斜树,查找时间复杂度为O(n),这等同于顺序查找。因此,如果我们希望对一个集合按二叉排序树查找,最好是把它构建成一棵平衡的二叉排序树。这样我们就引申出另一个问题,如何让二叉排序树平衡的问题。
右旋操作:
在这里插入图片描述

/*对以p为根的二叉排序树作右旋处理*/
/*处理之后p指向新的树根结点,即旋转处理之前的左子树的根结点*/
void R_Rotate(BiTree *P) {
  BiTree L;
  L = (*P)->lchild;         /*工指向P的左子树根结点*/
  (*P)->lchild = L->rchild; /*L的右子树挂接为卫的左子树*/
  L->rchild - (*P);
  *P = L; /*卫指向新的根结点*/
}

左旋操作:

/*对以p为根的二叉排序树作左旋处理*/
/*处理之后p指向新的树根结点,即旋转处理之前的右子树的根结点*/
void L_Rotate(BiTree *P) {
  BiTree R;
  R = (*P)->rchild;         /*工指向P的左子树根结点*/
  (*P)->rchild = R->lchild; /*L的右子树挂接为卫的左子树*/
  R->lchild - (*P);
  *P = R; /*卫指向新的根结点*/
}

两次旋转情况:
在这里插入图片描述

当增加结点10时,结构无变化,如图8-7-7的图10。再增加结点9,此时结点7的BF变成了-2,理论上我们只需要旋转最小不平衡子树7、9、10即可,但是如果左旋转后,结点9就成了10的右孩子,这是不符合二叉排序树的特性的,此时不能简单的左旋,如图11所示。
仔细观察图11,发现根本原因在于结点7的BF是-2,而结点10的BF是1,也就是说,它们俩一正一负,符号并不统一,而前面的几次旋转,无论左还是右旋,最小不平衡子树的根结点与它的子结点符号都是相同的。这就是不能直接旋转的关键。那怎么办呢?
不统一,不统一就把它们先转到符号统一再说,于是我们先对结点9和结点10进行右旋,使得结点10成了9的右子树,结点9的BF为-1,此时就与结点7的BF值符号统一了,如图8-7-7的图12所示。
这样我们再以结点7为最小不平衡子树进行左旋,得到图8-7-8的图13。接着插入8,情况与刚才类似,结点6的BF是-2,而它的右孩子9的BF是1,如图14,因此首先以9为根结点,进行右旋,得到图15,此时结点6和结点7的符号都是负,再以6为根结点左旋,最终得到最后的平衡二叉树,如图8-7-8的图16所示。

在这里插入图片描述

红黑树
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值