二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树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树
在上一篇博文中我们提到了,如果对普通二叉查找树进行随机的插入.删除,很可能导致树的严重不平衡 所以这一次,我们就来介绍一种最老的.可以实现左右子树"平衡效果"的树(或者说算法),即 ...
随机推荐
- nodemon:让node自动重启
nodemon:服务器自动重启工具 当我们修改代码时,node必须要手动重启,但可以按照nodemon. npm install -g nodemon 安装完 nodemon 后,就可以用 nodem ...
- 一些js的小技巧
这里收集了一些编码上的小技巧,大家可以学习学习. 1.浮点转整型 使用|0快速转换 var a=(12.002)|0;//12 使用~~快速转换 ~取反运算符,2=0010,~2=1101,因为第一位 ...
- DDLog设置方法
CHENYILONG Blog DDLog设置方法 本文永久地址为http://www.cnblogs.com/ChenYilong/p/3984246.html,转载请注明出处. 201 ...
- Codeforces 238 div2 A. Gravity Flip
题目链接:http://codeforces.com/contest/405/problem/A 解题报告:有n列箱子竖直放置,每列箱子上都有数量不等的箱子,这些箱子之间没有固定,当重力方向改为平行向 ...
- mysql 1045 access denied for user 解决方法
提示:1045 access denied for user 'root'@'localhost' using password yes方法一: # /etc/init.d/mysql stop # ...
- 【干货】查看windows文件系统中的数据—利用簇号查看文件与恢复文件
前面我们使用这个软件发现了很多删除掉的数据,今天来看看簇.FAT文件系统中,存在一个簇的链接,我知道了簇1在哪里就可以顺藤摸瓜恢复所有的信息. 这里使用FAT 12为例子,FAT其他万变不离其宗,甚至 ...
- python小记
最近有匹骚猪用微信骚扰我,我很是气愤, 自学一波脚本: 学习目的:用脚本回击回去,通过py写一个脚本,一次性给别人发n条消息: mac上自学python: brew install python3(自 ...
- C# wpf 阻止*和|的输入
private void texBox_KeyDown(object sender, KeyEventArgs e) { if (Keyboard.Modifiers == ModifierKeys. ...
- laravel中短信发送验证码的实现方法
在阿里云上开通短信服务后需要做的: 1,申请签名 2,申请模板 3,创建Accesskey ,值得说的是,可以通过阿里云提供的子用户进行Accesskey的创建,这样可以更安全 4,充值 larave ...
- Mysql报Cannot load from mysql.proc. The table is probably corrupted
1548-Cannot load from mysql.proc. The table is probably corrupted http://bugs.mysql.com/bug.php?id=5 ...