BST二叉查找树
2015-11-13 01:44
169 查看
博客链接:http://blog.csdn.net/lyh__521/article/details/49811149
二叉排序树包含以下域:
特点
设 x 为二叉排序树中的一个结点。如果y是x的左子树中的一个结点,则( y->key) <= (x->key);如果y是x的右子树中的一个结点,则(y->key) >= (x->key)。
即任意一个结点的左子树的所有关键字都比他小,右子树的所有关键字都比他大。
关键字有序性
使用中序遍历算法可将一棵BST树的所有关键字按照从小到大的顺序输出。
所以查找操作最多不会大于树的高度。时间复杂度为O(h),h为树的高度。
例如:查找 13
递归查找
非递归查找
如下图:
如下图:
有时我们需要找到某一个结点 x 的后继结点,即下一个大于 x 关键字的结点。因为结点右子树的所有关键字都比他大,所以如果 x 有右子树,只需要找出右子树的最小关键字。如果x 没有右子树,因为结点的所有左子树的关键字都偏小,所以我们只需要沿着 x 的父结点不断向上找,直到找到一个结点 y ,使得 x 属于 y 的左子树即可,此时y 是 x 的后继。如果返回NULL,说明 x->key 已经是树的最大关键字了。
例如,下图分别查找13(没有右子树)和15的后继:13–>15 、15–>17
前趋
即前一个比 x 小的关键字为 x 的前趋。可以模仿求后继的方法。
从根结点开始,沿树下降,指针 x 跟踪了这条路径,而 p 始终指向 x 的父结点。如果是空树,直接将插入的结点设置为根结点。否则,根据插入值与 x->key 比较的结果,决定继续向左走还是向右走,直到 x 变为NULL,NULL的位置就是我们要插入的位置。
根据要删除结点 z 的位置,主要分为以下3种情况:
情况1 : z 没有左孩子和右孩子
直接删除 z 结点,将 z 所在的位置置为NULL。
情况2 :z 只有一个孩子(左孩子或者右孩子)
只需要直接删除 z ,让 z 的孩子与 z 的父结点相连。将 z 的孩子移到 z 原来的位置即可。
情况3:z 既有左孩子,又有右孩子
当 z 结点有两个孩子时,不会直接删掉 z ,而是找到 z 的后继结点,用后继结点的关键字替换掉 z 的关键字,并且删掉后继结点,如上图。因为后继结点 z 的右子树的最小关键字,用 后继替换 z ,就依然能保证 z 位置的左子树偏小,右子树偏大的性质。删掉后继结点的方法使用情况1 或 情况 2。
因为只有 z 有两个孩子时才需要求后继结点,那么 z 此时必然是有右孩子的,所以z 的后继结点是 z 右树的最小关键字。不存在求后继时需要沿着父结点上升的情况。
介绍
如图1就是一颗二叉排序树,也就是二叉查找树,又称二叉搜索树,简称BST二叉排序树包含以下域:
成员 | 含义 |
---|---|
left | 指向左孩子 |
right | 指向右孩子 |
key | 关键字 |
parent | 指向父结点 |
设 x 为二叉排序树中的一个结点。如果y是x的左子树中的一个结点,则( y->key) <= (x->key);如果y是x的右子树中的一个结点,则(y->key) >= (x->key)。
即任意一个结点的左子树的所有关键字都比他小,右子树的所有关键字都比他大。
关键字有序性
使用中序遍历算法可将一棵BST树的所有关键字按照从小到大的顺序输出。
//中序遍历 void INORDER_Tree_Walk(BS_Tree* root) { if(root != NULL) { INORDER_Tree_Walk(root->left); //遍历左子树 print(root->key); //输出关键字 INORDER_Tree_Walk(root->right); //遍历右子树 } }
查询二叉查找树
从根结点开始查找,对经过的每个结点 x 与 要查询的k 比较,如果 k < (x->key) ,则沿着左子树下降;如果 k > (x->key) ,则沿着右子树下降 。未找到,返回NULL。所以查找操作最多不会大于树的高度。时间复杂度为O(h),h为树的高度。
例如:查找 13
递归查找
BS_Tree* Tree_Search(BS_Tree* root,int k) { //碰到空结点 或 查询成功 if(root == NULL || k == root->key ) return x; //大于要查询的关键字 else if(k < root->key) //走向左子树 return Tree_Search(root->left,k); else //走向右子树 return Tree_Search(root->right,k); }
非递归查找
BS_Tree* ITERATIVE_Tree_Search(BS_Tree* root,int k) { BS_Tree* x = root; while(x != NULL && k != x->key) { if(k < x->key) x = x->left; else x = x->right; } return x; }
最大关键字
如果要查找整棵二叉查找树的最大关键字,只要从根结点开始,一直沿着各结点的right 指针(向右)走即可。如下图:
//最大关键字 BS_Tree* Tree_MAXIMUM(BS_Tree* x) { //从 x 结点出发 while(x->right != NULL) x = x->right; //走向右子树 return x; }
最小关键字
如果要查找整棵二叉查找树的最小关键字,只要从根结点开始,一直沿着各结点的left 指针(向左)走即可。如下图:
//最小关键字 BS_Tree* Tree_MINIMUM(BS_Tree* x) { while(x->left != NULL) x = x->left; return x; }
前趋与后继
后继有时我们需要找到某一个结点 x 的后继结点,即下一个大于 x 关键字的结点。因为结点右子树的所有关键字都比他大,所以如果 x 有右子树,只需要找出右子树的最小关键字。如果x 没有右子树,因为结点的所有左子树的关键字都偏小,所以我们只需要沿着 x 的父结点不断向上找,直到找到一个结点 y ,使得 x 属于 y 的左子树即可,此时y 是 x 的后继。如果返回NULL,说明 x->key 已经是树的最大关键字了。
例如,下图分别查找13(没有右子树)和15的后继:13–>15 、15–>17
//后继 BS_Tree* Tree_SUCCESSOR(BS_Tree* x) { if(x->right != NULL) //x有右子树 return Tree_MINIMUM(x->right); //返回右子树的最小关键字 BS_Tree* y = x->parent; while(y != NULL && x == y->right) //x 不属于父结点的左子树 { x = y; //x 上移到父结点的位置 y = y->parent; } return y; }
前趋
即前一个比 x 小的关键字为 x 的前趋。可以模仿求后继的方法。
插入
插入比较简单,跟着代码注释走一遍就懂了。//插入 void Tree_Insert(BS_Tree* root,BS_Tree* z) { BS_Tree* p = NULL; BS_Tree* x = root; //从根结点开始查找 while(x != NULL) //x 没走到尽头 { p = x; //p 始终指向x的父结点 if(z->key < x->key) //当前结点大于插入值 x = x->left; //x 向左转前进 else x = x->right; //x 向右转前进 } z->parent = p; if(p == NULL) //这是一棵空树 root = z; //设置新插入的结点为根结点 else if(z->key < p->key) p->left = z; //将z 插入到左边 else p->right = z; //将z 插入到右边 }
从根结点开始,沿树下降,指针 x 跟踪了这条路径,而 p 始终指向 x 的父结点。如果是空树,直接将插入的结点设置为根结点。否则,根据插入值与 x->key 比较的结果,决定继续向左走还是向右走,直到 x 变为NULL,NULL的位置就是我们要插入的位置。
删除
因为要保持二叉查找树的性质,删除操作略有点麻烦。根据要删除结点 z 的位置,主要分为以下3种情况:
情况1 : z 没有左孩子和右孩子
直接删除 z 结点,将 z 所在的位置置为NULL。
情况2 :z 只有一个孩子(左孩子或者右孩子)
只需要直接删除 z ,让 z 的孩子与 z 的父结点相连。将 z 的孩子移到 z 原来的位置即可。
情况3:z 既有左孩子,又有右孩子
当 z 结点有两个孩子时,不会直接删掉 z ,而是找到 z 的后继结点,用后继结点的关键字替换掉 z 的关键字,并且删掉后继结点,如上图。因为后继结点 z 的右子树的最小关键字,用 后继替换 z ,就依然能保证 z 位置的左子树偏小,右子树偏大的性质。删掉后继结点的方法使用情况1 或 情况 2。
BS_Tree* Tree_Delete(BS_Tree* root,BS_Tree* z) { if(z->left == NULL || z->right == NULL) //没孩子或有一个孩子 BS_Tree* y = z; else y = Tree_SUCCESSOR(z); //z 有两个孩子时求后继 //y 指向真正要删除的结点 if(y->left != NULL) //如果y有左子树 BS_Tree* x = y->left; //x 指向左树 else x = y->right; //x 指向右树 //y没有孩子时,x=NULL if(x != NULL) //如果y有一个孩子 x->parent = y->parent; //设置y的孩子的父亲为y的父结点 if(y->parent == NULL) //要删除的是根,必只有一个孩子 root = x; //设置孩子为新的根 else if(y = (y->parent)->left) //否则如果y是左树 (y->parent)->left = x; //设置y的孩子x为左树 else (y->parent)->right = x; if(y != z) //如果y是后继 z->key = y->key; //用y的关键字替换z return y; //返回真正删除的位置 }
因为只有 z 有两个孩子时才需要求后继结点,那么 z 此时必然是有右孩子的,所以z 的后继结点是 z 右树的最小关键字。不存在求后继时需要沿着父结点上升的情况。
相关文章推荐
- 二叉查找树
- AVL树-自平衡二叉查找树(Java实现)
- “百度与站长”更新:关于网站收录,删除,seo等
- 用vbs删除某些类型文件和磁盘空间报告的脚本
- QQ聊天记录删除了怎么恢复简单方法
- vbs删除注册表项的代码
- 迅速删除非法文件名的批处理代码
- 通过批处理实现删除运行、查找等处的历史记录的代码
- Shell中删除某些文件外所有文件的3个方法
- 删除文件提示文件正在被另一个人或程序使用的解决方法
- 关于.LDB文件 .ldb文件的产生 .ldb文件的删除方法
- asp 合并记录集并删除的sql语句
- SQLserver 数据库危险存储过程删除与恢复方法
- sql自增长设置与删除的深入分析
- 使用 Iisext.vbs 删除 Web 服务扩展文件的方法
- linux oracle数据库删除操作指南
- jQuery删除一个元素后淡出效果展示删除过程的方法
- 使用 Iisftpdr.vbs 删除FTP虚拟目录(支持本地与远程)
- 必须会的SQL语句(四) 数据删除和更新
- mssql SA帐号的改名和删除