二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树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树
在上一篇博文中我们提到了,如果对普通二叉查找树进行随机的插入.删除,很可能导致树的严重不平衡 所以这一次,我们就来介绍一种最老的.可以实现左右子树"平衡效果"的树(或者说算法),即 ...
随机推荐
- 在angular中利用分页插件进行分页
必需:angular分页js和css 当然还有angular.js 还需要bootstrap的css angular.min.js (下面我直接把插件粘贴上去了,以免有的同学还要去找.是不是很贴 ...
- spring注解 @Scheduled(cron = "0 0 1 * * *")实现定时的执行任务
@Scheduled(cron = "0 0 1 * * *") 在使用该注解以前请做好以下准备工作,配置好相应的xm文件. 配置定时注解的步骤:http://blog.csdn. ...
- 无锁并发框架Disruptor学习入门
刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...
- 搭建RabbitMQ集群(通用)
RabbitMQ在Erlang node(节点)上 Erlang天生具有集群特性,非常好搭建集群,每一个节点(node)上具有一个叫erlang.Cookie的东西,也是一个标识符,可以互认. 1). ...
- ARMCC和GCC编译ARM代码的软浮点和硬浮点问题【转】
转自:https://blog.csdn.net/hunanchenxingyu/article/details/47003279 本文介绍了ARM代码编译时的软浮点(soft-float)和硬浮点( ...
- 大数据系列之并行计算引擎Spark介绍
相关博文:大数据系列之并行计算引擎Spark部署及应用 Spark: Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎. Spark是UC Berkeley AMP lab ( ...
- 页面跳转时中间参数保存(memcache/cookie)
2014年1月19日 17:30:27 我这篇文章就说了一句话:用cookie保存页面间跳转时的参数 情景: 客服在后台操作的时候,经常从列表页进入到编辑页,编辑完信息后,还要自动返回之前的列表页 问 ...
- Android: 详解触摸事件如何传递
当视图的层次结构比较复杂的时候,触摸事件的响应流程也变得复杂. 举例来说,你也许有一天想要制作一个手势极其复杂的 Activity 来折磨你的用户,你经过简单思索,认为其中应该包含一个 PageVie ...
- python enumrate使用
新接触了一个函数 enumrate ,很多情况下我们想获得可迭代的容器(例如dict.list.tuple等)元素的时候,想同时获得一个序号用以他用. 代码常常写成这个样子 list_a = [&qu ...
- Java基础86 MySQL数据库基础知识
本文知识点(目录): 1.MySQL数据库的概述 2.MySQL数据库的管理[对数据库的操作](查询.删除.创建数据库,以及查询和修改数据库的编码模式) 3.表的管理[对数据库 表的操作] ...