二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树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树
在上一篇博文中我们提到了,如果对普通二叉查找树进行随机的插入.删除,很可能导致树的严重不平衡 所以这一次,我们就来介绍一种最老的.可以实现左右子树"平衡效果"的树(或者说算法),即 ...
随机推荐
- *p++、(*p)++、*++p、++*p 的区别
int a[5]={1,2,3,4,5};int *p = a; *p++ 先取指针p指向的值(数组第一个元素1),再将指针p自增1: cout << *p++; // 结果为 1 cou ...
- 被误解的 Node.js
http://www.ibm.com/developerworks/cn/web/1201_wangqf_nodejs/ 被误解的 Node.js
- 各浏览器 position: fixed 造成的bug 通用解决办法,Safari, iOS
将原来使用 position: fixed 的元素外层包裹一个 div.fixedWrapper .fixedWrapper { width: 100%; overflow: hidden; pos ...
- Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】
原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://bl ...
- pip 18.1: pipenv graph results in ImportError: cannot import name 'get_installed_distributions'
I'm currently using python3 -m pip install pip==10.0.1python3 -m pip install pipenv==2018.5.18 Once ...
- python2脚本批量转成python3
#查看 模块的安装路径 import tools print (tools.__file__) #进入cmd,然后python 2to3.py -w 需要批量转换的文件所在的路径#参考:http:// ...
- tensorflow variable的保存和修改(加载一部分variable到新的model中)
link: https://www.tensorflow.org/guide/saved_model 中文博客:https://blog.csdn.net/Searching_Bird/article ...
- npm tls证书报错
Error: unable to verify the first certificate 设置node环境(零时使用,再次使用需要重新使用) set NODE_TLS_REJECT_UNAUTHOR ...
- day25作业
1.阻塞 2.就绪 3.阻塞 4.Runnable 5.join() 6.synchronized 7.notify()和notifyAll() 8.Object 1.A 2.D ...
- sonarLint--强大的代码审查工具(插件)
idea也有的一个插件 贴上一个eclipse的sonarlint用法 http://blog.csdn.net/limm33/article/details/51166840 不过听说从2015年1 ...