上一节我们介绍了二分(折半)查找,也了解了它的优缺点。

二分查找的特点:二分查找能够提高有序表中数据元素的查找速度;二分查找的时间复杂度为O(log2n);二分查找是一种静态查找

二分查找的不足:当查找表经常变化时,二分查找的整体性能急剧下降。

二分查找的硬伤:二分查找基于有序表。

当需要插入或者删除数据元素时,为了能够继续进行二分查找,需要大规模挪动有序表中的数据元素,使得插入或者删除后的线性表保持有序。二分查找的过程是一棵二叉树!如下图:


这颗二叉树的特性如下:
1.任意一个结点的值都大于其左子树的所有结点值;

2.任意一个结点的值都小于其右子树的所有结点值。

如何改进二分查找使其适应动态查找?这里就有了一个新的想法,直接组织一棵具有二分查找特性的二叉树。二分查找过程即变换为对树结点的查找过程;由二分查找的特性可知树结点查找的时间复杂度为O(log2n);只在叶结点处插入新结点即可保持特性不变;删除树结点时也可以容易的保持特性不变。这是一棵特殊的二叉树,怎么给它命名呢?它就是二叉排序树

二叉排序树的定义:二叉排序树是一棵空树,或者,若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左右子树也分别是二叉排序树。

二叉排序树是特殊的二叉树,因此具有与二叉树相同的操作。只不过是插入与删除时与普通的二叉树有所不同而已,下面介绍一下二叉排序树的插入与删除操作:

二叉排序树的插入和删除操作

1.插入:其插入操作总是在叶结点处进行;

2.删除;

2.1.叶结点:直接删除;

2.2.非叶结点:查找合适的替代者后删除。

二叉排序树的所有操作都必须保证其二叉排序性不变。

那么,如何为删除操作查找合适的替代者:

1.有一个孩子的结点:用孩子结点代替原结点;

2. 有两个孩子的结点:用中序遍历下的直接前驱替换原结点。

下面介绍一下二叉排序树的插入与删除实现代码:

1.插入

根据上面的描述,我们知道二叉排序树的插入需要递归来实现,所以首先看一下二叉排序树插入的递归函数

// 二叉排序树插入递归函数
static int recursive_insert(BSTreeNode* root, BSTreeNode* node, BSTree_Compare* compare)
{int ret = 1;int r = compare(node->key, root->key);// 二叉排序树中含有相同值,非法if( r == 0 ){ret = 0;}// 插入元素大于根结点// 在左子树位置插入else if( r < 0 ){// 左子树不为空、调用二叉排序树插入递归函数,直至插入至左子树if( root->left != NULL ){ret = recursive_insert(root->left, node, compare);}// 左子树为空,直接插入else{root->left = node;}}// 插入元素小于根结点//  在右子树位置插入else if( r > 0 ){// 右子树不为空、调用二叉排序树插入递归函数,直至插入至右子树if( root->right != NULL ){ret = recursive_insert(root->right, node, compare);}// 右子树为空,直接插入else{root->right = node;}}
}

插入代码如下:

// 根据参数插入结点至二叉排序树
int BSTree_Insert(BSTree* tree, BSTreeNode* node, BSTree_Compare* compare)
{// 定义二叉排序树结构体变量并强制转换参数TBSTree* btree = (TBSTree*)tree;// 入口参数合法性检查int ret = (btree != NULL) && (node != NULL) && (compare != NULL);    // 入口参数合法性okif( ret ){node->left = NULL;node->right = NULL;// 插入位置为根结点if( btree->root == NULL ){btree->root = node;}// 调用二叉排序树递归函数,寻找插入位置else{ret = recursive_insert(btree->root, node, compare);}// 二叉排序树结点个数加1if( ret ){btree->count++;}}return ret;
}

插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或者右孩子结点。同插入一样,二叉排序树的删除也要通过递归来实现,递归函数如下:

static BSTreeNode* delete_node(BSTreeNode** pRoot)
{BSTreeNode* ret = *pRoot;// 有一个孩子的结点,用孩子结点代替原结点// 右孩子为空,有一个左孩子的结点,用左孩子结点代替原结点if( (*pRoot)->right == NULL ){*pRoot = (*pRoot)->left;}// 左孩子为空,有一个右孩子的结点,用右孩子结点代替原结点else if( (*pRoot)->left == NULL ){*pRoot = (*pRoot)->right;}// 有两个孩子的结点else{BSTreeNode* g = *pRoot;             // 保存要删除结点的地址BSTreeNode* c = (*pRoot)->left;     // 保存要删除结点的左孩子结点地址// 循环移动要删除结点的左孩子的右孩子,直至到叶结点为止,转左,然后向右到尽头// 相当于用中序遍历下的直接前驱while( c->right != NULL ){g = c;c = c->right;        // 一直查找右孩子}// 要删除结点的左孩子有右孩子,用最后右叶子结点if( g != *pRoot ){g->right = c->left;}// 要删除结点的左孩子没有右孩子,直接用左孩子代替要删除结点else{g->left = c->left;}// 将要删除结点的双亲结点的左右孩子结点指向该结点的左右孩子结点c->left = (*pRoot)->left;c->right = (*pRoot)->right;*pRoot = c;}return ret;
}// 删除二叉排序树结点递归函数
static BSTreeNode* recursive_delete(BSTreeNode** pRoot, BSKey* key, BSTree_Compare* compare)
{BSTreeNode* ret = NULL;    // 参数合法,树存在if( (pRoot != NULL) && (*pRoot != NULL) ){// 返回关键字与根结点比较结果int r = compare(key, (*pRoot)->key);        // 找到关键字,调用删除结点函数删除结点if( r == 0 ){ret = delete_node(pRoot);}// 获取元素大于根结点// 调用删除二叉排序树结点递归函数,从左子树中开始else if( r < 0 ){ret = recursive_delete(&((*pRoot)->left), key, compare);}// 获取元素小于根结点// 调用删除二叉排序树结点递归函数,从右子树中开始else if( r > 0 ){ret = recursive_delete(&((*pRoot)->right), key, compare);}}return ret;
}

删除结点函数如下:

// 删除二叉排序树指定结点
BSTreeNode* BSTree_Delete(BSTree* tree, BSKey* key, BSTree_Compare* compare)
{// 定义二叉排序树结构体变量并强制转换参数TBSTree* btree = (TBSTree*)tree;BSTreeNode* ret = NULL;     // 入口参数合法性检查okif( (btree != NULL) && (key != NULL) && (compare != NULL) ){// 调用删除二叉排序树结点递归函数ret = recursive_delete(&btree->root, key, compare);// 二叉排序树结点个数减1if( ret != NULL ){btree->count--;}}return ret;
}

由于二叉排序树的其他操作和普通二叉树相同,这里不再赘述,详请参考二叉树的创建

二叉排序树整体代码:二叉排序树的C代码实现

数据结构之二叉排序树相关推荐

  1. C语言数据结构之二叉排序树

    C语言数据结构之二叉排序树 tips:前些天学习了线索二叉树,今天来总结一下二叉排序树的相关操作. 二叉排序树(BST): 若左子树非空,则左子树上所有结点的值均小于根结点的值: 若右子树非空,则右子 ...

  2. 王道数据结构自学-二叉排序树(二叉查找树)

    王道数据结构自学-二叉排序树(二叉查找树) 二叉排序树就是将比结点大的值放在右结点,比结点小的值放在左结点. // 构建树的结构 typedef int KeyType; typedef struct ...

  3. 【数据结构】二叉排序树

    二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树. 特点   二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: 1.若左子树不 ...

  4. 【Java数据结构】二叉排序树

    二叉排序树 为什么需要二叉排序树? 什么是二叉排序树(BST)? 二叉排序树代码实现 二叉排序树添加节点 二叉排序树查找节点 二叉排序树删除节点 搜索父节点 删除一颗树中最小的节点并获取值 删除没有子 ...

  5. 数据结构之二叉排序树(C++实现)

    目录 非递归方法: 递归方法: 递归查找: 删除节点 中序遍历: 二叉树是数据结构中的一个非常非常重要的板块,俗话说不到长城非好汉,那么不会二叉树就不算了解数据结构.什么是二叉树,在之前我们了解了链表 ...

  6. 一层循环时间复杂度_数据结构:二叉排序树的前/中/后序遍历(递归与循环两种版本)...

    树的设计初衷与操作时间复杂度 树这种数据结构的出现主要是对链表数据结构的优化,链表数据结构是线性结构,操作一般需要O(N)的时间复杂度,树是链表的变形,即链表的每个节点包含一个节点,而树的节点可以包含 ...

  7. 大话数据结构 : 二叉排序树

    二叉排序树 二叉排序树的好处在于插入 删除 查找的效率很高,比线性表和数组都好 二叉树稍微难一点的敌方在于删除,在删除一个既有左子树也有右子树的节点时比较麻烦,策略就是将要删除的节点的左子树中向右查找 ...

  8. 数据结构:二叉排序树

    1.二叉排序树 二叉排序树:BST( Binary Sort( Search)Tree,又称二叉查找树(Binary Search Tree),亦称二叉搜索树.对于二叉排序树的任何一个非叶子节点,要求 ...

  9. 数据结构(十)二叉排序树

    1.算法流程 (1)树的构建与插入.查找 二叉排序树主要是通过逐个节点插入的方式进行构建树:每插入一个新节点p的时候,从根节点开始判断key值大小,确定往左走还是往右走,逐步递归,直到走到叶子节点,无 ...

最新文章

  1. iOS 相册和网络图片的存取
  2. 合格的CTO应该是什么样?雷军王海峰王小川等共谈「技术创新」| CNCC2020
  3. 在windows下使用llvm+clang
  4. wxWidgets:日志概述
  5. 在Mac里给Terminal终端自定义颜色
  6. iNeuOS工业互联网操作系统,矿山动态产量计量系统和铁路车辆识别系统应用场景案例...
  7. 【飞秋】网络的基础-域名篇
  8. 返回一个首尾相接的二维整数数组中最大子数组的和
  9. “RPC好,还是RESTful好?”,这个问题不简单!
  10. 103. SPL 标志库
  11. 第七章思维导图前半段
  12. vue 之 ele上传文件 和 下载文件
  13. Frank and Hall
  14. JetBrain的哪款产品能够编写C++和C的代码啊?
  15. FFmpeg[32] - x264 [error]: high422 profile doesn‘t support lossless
  16. 关于OFDM中的FFT和IFFT
  17. RK3399 Android7.1修改安兔兔等读到的cpu最大频率为1.992GHz
  18. 高通Q+A平台 android gcore解析环境搭建
  19. 转:变种挖矿蠕虫问题解决
  20. 1688商品详情SKU

热门文章

  1. 特征筛选8——递归特征删除(REF)筛选特征(有监督筛选)
  2. 使用LDA模型对新的文档进行分类
  3. linux的基本使用方法,Linux/Unix基本使用方法
  4. 死循环线程php,QObject的派生类方法实现多线程死循环问题
  5. mysql默认存储引擎的索引结构是_InnoDB引擎的索引和存储结构
  6. python async socket_Python开发中常用的标准库,这些都是你应该掌握的
  7. 文件服务器无法上传资料,该文件未上传至服务器怎么回事
  8. 计算机窗口设计java实验,Java银行取款异常处理计算器设计图形用户界面设计实验报告.doc...
  9. 查看一个进程对应的端口号
  10. iis configuration error