基本数据结构之二叉查找树

写在之前的话: 无话可说
树: 有且只有一个称为根的节点, 有若干个互不相交的子树,这些子树本身也是一棵树。
二叉树: 任意一个节点的子节点个数最多两个,且子节点的位置不可更改(即左右子树有顺序之分)。
满二叉树: 在不增加树的层数的前提下,无法再多添加一个节点的二叉树就是满二叉树(除了叶节点(就是最下面的节点了)外其余节点都有两个左右子节点)。
完全二叉树:如果只删除了满二叉树最底层最右边的连续若干个节点,这样形成的二叉树就是完全二叉树。

树的存储:树的存储有很多种,这边只介绍一种链式存储,每个节点对应一个C语言中的struct结构类型的对象,比如    

typedef struct BSNode{
  int key;
  struct BSNode *left;
  struct BSNode *right;
  struct BSNode *parent;
} *BSTree, BSNode;

定义:二叉查找树是一种数据结构,首先是一棵二叉树,并且满足性质:
1.根节点的关键值大于等于它的左子树的根节点(就是它的左子节点)的关键值
2.根节点的关键值小于等于它的右子树的根节点(就是它的右子节点)的关键值


作用: 存在即合理,万物存在必有它的原因或者它产生的目的。二叉查找树产生的原因就是用于减少查找的时间的。(下面介绍)


二叉查找树的集合操作包括: 
SEARCH MINIMUM MAXIMUM PREDECESSOR(前一个) SUCCESSOR(后一个) INSERT DELETE

主要的SEARCH操作:

BSNode * bstree_search(BSNode* x, int key) {
  printf ("%d\n", x -> key);
  if (x == NULL || key == x->key) {
    return x;
  } else {
    if (key < x->key) {
      return bstree_search(x->left, key);
    } else {
      return bstree_search(x->right, key);
    }
  }
}

因为二叉查找树按照它的性质,每个节点的左子树的键值都是小于等于它的右子树节点的键值,所以每次比较都是先比较根节点,然后决定是继续与左子树的节点比较还是右子树的节点比较,所以如果二叉查找树是一棵完全二叉树的话,它的高度是lg(n)向上取整,而比较的次数的最坏情况就是它的高度,但是如果一棵二叉查找树的根节点只有左子树或者右子树的话那么它比较的次数的最坏情况就是二叉查找树的节点数目了。

MINIMUM

BSNode * bstree_minimum(BSNode *x) {
  while (x->left != NULL) {
    x = x -> left;
  }
  return x;
}
二叉查找树的最小关键值就是它的最左节点的关键值


MAXIMUM

BSNode * bstree_maximum(BSNode *x) {
  while (x->right != NULL) {
    x = x -> right;
  }
  return x;
}
同理,最大关键值就是它的最右子节点


PREDESESSOR

BSNode * bstree_predecessor(BSNode *x) {
  if (x -> left != NULL) {
    return bstree_maximum(x -> left);
  }
  BSNode *y = x -> parent;
  while (y != NULL && y -> left == x) {
    x = y;
    y = y -> parent;
  }
  return y;
}
某节点的前继分两中情形:
1. 如果该节点存在左结点,则左节点的就是该节点的前继
2. 如果该节点Z不存在左节点,则前继的查找过程为不断的向父节点查找,直至某个节点是它父节点的右子节点为止。因为如果如果节点一直是其左节点的话,那么它的键值肯定是大于左子树的节点的,直到遇到某个节点A是它父节点B的右子节点,因为A >= B, 所以而Z是除了A之外最小的节点,所以A >= Z >= B


SUCCESSOR

BSNode * bstree_successor(BSNode *x) {
  if (x -> right != NULL) {
    return bstree_minimum(x -> right);
  }
  BSNode *y = x -> parent;
  while (y != NULL && y -> right == x) {
    x = y;
    y = y -> parent;
  }
  return y;
}
同前继对称,后继也分两种情形
1. 如果该节点存在右子结点,则右节点的就是该节点的后继
2. 如果该节点Z不存在右子节点,则后继的查找过程为不断的向父节点查找,直至某个节点是它父节点的左子节点为止。论证同上。


INSERT

void bstree_insert(BSNode **root,  BSNode *z) {
  BSNode *parent = NULL; //父节点
  BSNode *x = *root;
  while (x != NULL) {
    parent = x;
    if (x -> key < z -> key) {
      x = x -> right;      
    } else {
      x = x -> left;
    }
  }
  z -> parent = parent;
  if (parent == NULL) *root = z;  //root
  else if (z -> key < parent -> key) parent -> left = z;
  else parent -> right = z;
}
插入的过程很简单,只是按照上面的查找,找到叶子节点的父亲节点,将之插入到左节点或者右节点中。

DELETE

删除某个节点A的情况分为三种
1. A没有子女,则修改A的父节点,使得NULL成为其子女
2. A只有一个子女,则建立一条A的父节点到A的字节点的一条链来删除A
3. A有两个子女,先删除A的后继Y,并将Y的KEY值替换成Z的KEY值

delete代码写的有点烂就不贴出来了,看图吧:




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值