二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用
这是一篇两年前写的东西,自我感觉还是相当不错的Treap教程。正好期末信息科学技术概论课要求交一个论文,就把这个东西修改了一下交了,顺便也发到这里吧。
1、序
详细实现了二叉查找树的各种操作:插入结点、构造二叉树、删除结点、查找、 查找最大值、查找最小值、查找指定结点的前驱和后继
2、二叉查找树简介
它或者是一棵空树;或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)左、右子树也分别为二叉排序树
3、二叉查找树的各种操作
此处给出代码,注释非常详细,具体操作请参考代码:
- /*************************************************************************
- 这是一个二叉查找树,实现了以下操作:插入结点、构造二叉树、删除结点、查找、
- 查找最大值、查找最小值、查找指定结点的前驱和后继。上述所有操作时间复杂度
- 均为o(h),其中h是树的高度
- 注释很详细,具体内容就看代码吧
- *************************************************************************/
- #include<stdio.h>
- #include<stdlib.h>
- //二叉查找树结点描述
- typedef int KeyType;
- typedef struct Node
- {
- KeyType key; //关键字
- struct Node * left; //左孩子指针
- struct Node * right; //右孩子指针
- struct Node * parent; //指向父节点指针
- }Node,*PNode;
- //往二叉查找树中插入结点
- //插入的话,可能要改变根结点的地址,所以传的是二级指针
- void inseart(PNode * root,KeyType key)
- {
- //初始化插入结点
- PNode p=(PNode)malloc(sizeof(Node));
- p->key=key;
- p->left=p->right=p->parent=NULL;
- //空树时,直接作为根结点
- if((*root)==NULL){
- *root=p;
- return;
- }
- //插入到当前结点(*root)的左孩子
- if((*root)->left == NULL && (*root)->key > key){
- p->parent=(*root);
- (*root)->left=p;
- return;
- }
- //插入到当前结点(*root)的右孩子
- if((*root)->right == NULL && (*root)->key < key){
- p->parent=(*root);
- (*root)->right=p;
- return;
- }
- if((*root)->key > key)
- inseart(&(*root)->left,key);
- else if((*root)->key < key)
- inseart(&(*root)->right,key);
- else
- return;
- }
- //查找元素,找到返回关键字的结点指针,没找到返回NULL
- PNode search(PNode root,KeyType key)
- {
- if(root == NULL)
- return NULL;
- if(key > root->key) //查找右子树
- return search(root->right,key);
- else if(key < root->key) //查找左子树
- return search(root->left,key);
- else
- return root;
- }
- //查找最小关键字,空树时返回NULL
- PNode searchMin(PNode root)
- {
- if(root == NULL)
- return NULL;
- if(root->left == NULL)
- return root;
- else //一直往左孩子找,直到没有左孩子的结点
- return searchMin(root->left);
- }
- //查找最大关键字,空树时返回NULL
- PNode searchMax(PNode root)
- {
- if(root == NULL)
- return NULL;
- if(root->right == NULL)
- return root;
- else //一直往右孩子找,直到没有右孩子的结点
- return searchMax(root->right);
- }
- //查找某个结点的前驱
- PNode searchPredecessor(PNode p)
- {
- //空树
- if(p==NULL)
- return p;
- //有左子树、左子树中最大的那个
- if(p->left)
- return searchMax(p->left);
- //无左子树,查找某个结点的右子树遍历完了
- else{
- if(p->parent == NULL)
- return NULL;
- //向上寻找前驱
- while(p){
- if(p->parent->right == p)
- break;
- p=p->parent;
- }
- return p->parent;
- }
- }
- //查找某个结点的后继
- PNode searchSuccessor(PNode p)
- {
- //空树
- if(p==NULL)
- return p;
- //有右子树、右子树中最小的那个
- if(p->right)
- return searchMin(p->right);
- //无右子树,查找某个结点的左子树遍历完了
- else{
- if(p->parent == NULL)
- return NULL;
- //向上寻找后继
- while(p){
- if(p->parent->left == p)
- break;
- p=p->parent;
- }
- return p->parent;
- }
- }
- //根据关键字删除某个结点,删除成功返回1,否则返回0
- //如果把根结点删掉,那么要改变根结点的地址,所以传二级指针
- int deleteNode(PNode* root,KeyType key)
- {
- PNode q;
- //查找到要删除的结点
- PNode p=search(*root,key);
- KeyType temp; //暂存后继结点的值
- //没查到此关键字
- if(!p)
- return 0;
- //1.被删结点是叶子结点,直接删除
- if(p->left == NULL && p->right == NULL){
- //只有一个元素,删完之后变成一颗空树
- if(p->parent == NULL){
- free(p);
- (*root)=NULL;
- }else{
- //删除的结点是父节点的左孩子
- if(p->parent->left == p)
- p->parent->left=NULL;
- else //删除的结点是父节点的右孩子
- p->parent->right=NULL;
- free(p);
- }
- }
- //2.被删结点只有左子树
- else if(p->left && !(p->right)){
- p->left->parent=p->parent;
- //如果删除是父结点,要改变父节点指针
- if(p->parent == NULL)
- *root=p->left;
- //删除的结点是父节点的左孩子
- else if(p->parent->left == p)
- p->parent->left=p->left;
- else //删除的结点是父节点的右孩子
- p->parent->right=p->left;
- free(p);
- }
- //3.被删结点只有右孩子
- else if(p->right && !(p->left)){
- p->right->parent=p->parent;
- //如果删除是父结点,要改变父节点指针
- if(p->parent == NULL)
- *root=p->right;
- //删除的结点是父节点的左孩子
- else if(p->parent->left == p)
- p->parent->left=p->right;
- else //删除的结点是父节点的右孩子
- p->parent->right=p->right;
- free(p);
- }
- //4.被删除的结点既有左孩子,又有右孩子
- //该结点的后继结点肯定无左子树(参考上面查找后继结点函数)
- //删掉后继结点,后继结点的值代替该结点
- else{
- //找到要删除结点的后继
- q=searchSuccessor(p);
- temp=q->key;
- //删除后继结点
- deleteNode(root,q->key);
- p->key=temp;
- }
- return 1;
- }
- //创建一棵二叉查找树
- void create(PNode* root,KeyType *keyArray,int length)
- {
- int i;
- //逐个结点插入二叉树中
- for(i=0;i<length;i++)
- inseart(root,keyArray[i]);
- }
- int main(void)
- {
- int i;
- PNode root=NULL;
- KeyType nodeArray[11]={15,6,18,3,7,17,20,2,4,13,9};
- create(&root,nodeArray,11);
- for(i=0;i<2;i++)
- deleteNode(&root,nodeArray[i]);
- printf("%d\n",searchPredecessor(root)->key);
- printf("%d\n",searchSuccessor(root)->key);
- printf("%d\n",searchMin(root)->key);
- printf("%d\n",searchMax(root)->key);
- printf("%d\n",search(root,13)->key);
- return 0;
- }
4、附录
参考书籍 《算法导论》
二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用的更多相关文章
- 【查找结构3】平衡二叉查找树 [AVL]
在上一个专题中,我们在谈论二叉查找树的效率的时候.不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这 ...
- 平衡二叉查找树 AVL 的实现
不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这个想法,平衡二叉树出现了. 平衡二叉树的定义 (A ...
- 006-数据结构-树形结构-二叉树、二叉查找树、平衡二叉查找树-AVL树
一.概述 树其实就是不包含回路的连通无向图.树其实是范畴更广的图的特例. 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 1.1.树的特性: 每个结点有零个或多个子 ...
- 平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】
平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是 ...
- 数据结构-自平衡二叉查找树(AVL)详解
介绍: 在计算机科学中,AVL树是最先发明的自平衡二叉查找树. 在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树. 查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- AVL树(平衡二叉查找树)
首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...
- 算法学习 - 平衡二叉查找树实现(AVL树)
平衡二叉查找树 平衡二叉查找树是非常早出现的平衡树,由于全部子树的高度差不超过1,所以操作平均为O(logN). 平衡二叉查找树和BS树非常像,插入和删除操作也基本一样.可是每一个节点多了一个高度的信 ...
- 二叉查找树、平衡二叉树(AVLTree)、平衡多路查找树(B-Tree),B+树
B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引. B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的. 在 ...
- 深入浅出数据结构C语言版(12)——平衡二叉查找树之AVL树
在上一篇博文中我们提到了,如果对普通二叉查找树进行随机的插入.删除,很可能导致树的严重不平衡 所以这一次,我们就来介绍一种最老的.可以实现左右子树"平衡效果"的树(或者说算法),即 ...
随机推荐
- JavaScript实现ajax发送表单数据
知识点: 1.重置表单数据 2.获取表单数据(纯JavaScript) 3.设置表单数据(纯JavaScript) 4.ajax发送数据到客户端 (1)设置请求头,自己组合数据 (2)实例化表单对象, ...
- Spark记录-Scala shell命令
1.scala shell命令 scala> :help All commands can be abbreviated, e.g., :he instead of :help. :edit & ...
- CM记录-HDFS清理垃圾回收站
HDFS数据块所在存储的目录满了的解决方法 1.增加磁盘空间 2.删除回收站 hadoop dfs -du -h 查看各个HDFS空间占用情况 hadoop dfs -expunge 清空回收站 ...
- JIRA项目管理搭建
部署JIRA 7.2.2 for Linux 转自:http://www.yfshare.vip/2017/05/09/%E9%83%A8%E7%BD%B2JIRA-7-2-2-for-Linux/ ...
- Nginx配置项优化(转载)
(1)nginx运行工作进程个数,一般设置cpu的核心或者核心数x2 如果不了解cpu的核数,可以top命令之后按1看出来,也可以查看/proc/cpuinfo文件 grep ^processor / ...
- 关于 xcode5 的no matching provisioning profiles found
CHENYILONG Blog 关于 xcode5 的no matching provisioning prof- about the question in xcode5 "no matc ...
- redis初使用
下载地址:https://redis.io/download Redis项目不正式支持Windows.但是,微软开放技术小组开发并维护了针对Win64的Windows端口 windows版下载地址:h ...
- Oracle 基本操作符
1.一般操作符 (1)!= 不等于 select empno,ename,job from scott.emp where job!='manager' (2)^= 不等于 select empno, ...
- Java练习之使用StringBuilder
package string.demo; /* * 将数组变为字符串输出 */ public class StringBuilderTest { /** * @param args */ public ...
- 基于Consul的数据库高可用架构【转】
几个月没有更新博客了,已经长草了,特意来除草.本次主要分享如何利用consul来实现redis以及mysql的高可用.以前的公司mysql是单机单实例,高可用MHA加vip就能搞定,新公司mysql是 ...