一、二叉搜索树

二叉搜索树的定义

二叉搜索树也被称为二叉排序树、二叉查找树

二叉搜索树:

(1)二叉树任意两个节点的关键字值不相同

(2)若左子树不为空,则左子树上所有结点的关键字值均小于根结点的关键字值

(3)若右子树不为空,则右子树上所有节点的关键字值均大于根节点的关键字值

(4)左,右子树也分别是二叉搜索树

如果以中序遍历一棵二叉搜索树,将得到一个以关键字值递增排列的有序序列

二叉搜索树的搜索

算法思想:

(1)若二叉搜索树为空,则查找失败,返回NULL

(2)若二叉搜索树不为空,则将给定值x与根节点关键字比较:①如果x=data,则查找成功。返回根节点地址②如果x>data,则接下来查找右子树③如果x<data,则接下来查找左子树

所以需要递归实现

#include<stdio.h>
#include<stdlib.h>typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;BTNode* BTSearch(BTNode* bt, BTDataType x)
{if (bt == NULL){return NULL;}if (bt->data==x){return bt;}else if (x<bt->data){return BTSearch(bt->left, x);}else{return BTSearch(bt->right, x);}
}

迭代实现;

//迭代实现
BTNode* BTSearch(BTNode* bt, BTDataType x)
{while (bt){if (x<bt->data){bt = bt->left;}else if (x>bt->data){bt = bt -> right;}else{return bt;}}return NULL;
}

查找次数取决于被查找节点的深度,也就是节点所在层数==查找次数

最好情况查找时间复杂度:O(log2N)

最坏情况查找时间复杂度;O(N)

平均查找时间复杂度取决于树的形态

二叉搜索树的插入

如果二叉树为空,则插入节点作为根节点插入到空树中

否则,继续在左右子树上查找

树中已经有,则不再插入

树中没有,则将元素查插入到搜索结束处

不同插入次序的序列生成不同形态的二叉排序树

bool BTInsert(BTNode* bt,BTDataType x)
{BTNode* p = bt, * q, * newnode;while (p){q = p;if (x<p->data){p = p->left;}else if (x>p->data){p = p->right;}else{printf("insert fail\n");return false;}}newnode = (BTNode*)malloc(sizeof(BTNode));newnode->data = x;newnode->left = NULL;newnode->right = NULL;if (bt==NULL){bt = newnode;}else if (x<q->data){q->left = newnode;}else{q->right = newnode;}return true;}

二叉搜索树的删除

需要保证删除节点之后,二叉搜索树仍然还是二叉搜索树①将因删除节点而断开的二叉链表重新链接起来②防止重新链接后树的高度增加

这种删除操作的具体实现步骤可描述如下.
首先搜索待删除元素所在的结点,记为结占n并记录n的双亲结点(。如果不存在待删除的元素,应返回NotPresent。如果存在待删除的元素,则删除结点p的操作可分下面两种情况讨论。 
(1)若结点p有两棵非空子树,这时需搜索p的中序遍历次序下的直接后继(或直接前驱)结点,设为结点s。然后将s的值复制到待删除结点p中,称为替代。因为结点s最多只有一棵非空子树,这样一来,问题便转化为“被删除的结点最多只有一棵非空子树”的问题。
(2)当结点p只有一棵非空子树或p是叶结点时,若p->LChild非空,则由p->LChild取代p,否则由p->RChild取代p。事实上,若p是叶结点,将以NULL取代p. 
程序中用以取代p 的结点由指针c指示,c可以为空指针。若被删除的结点原来是根结点,则删除后,用来取代它的结点(可以是空树)成为新的根结点。一个被删除的结点,如果原来是其双亲的左孩子,则取代它的结点(子树)也应成为该双亲的左孩子(左子树),反之亦然。最后需要使用free语句释放结点p所占用的空间。

//二叉搜索树的插入
bool BTDelete(BTNode* bt,BTDataType x)
{BTNode* c, * r, * s, * p = bt, * q;while (p&&p->data!=x)//从根节点开始查找data=x的节点p{q = p;//q为p的双亲结点if (x<p->data){p = p->left;}else{p == p->right;}}if (p==NULL)//找不到被删除的节点就返回空{printf("Not Present\n");return false;}if (p->left&&p->right)//p的两个子树为空{s = p->right;//为p的右孩子r = p;while (s->left)//搜索p的中序直接后继结点{r = s;s = s->left;}p->data = s->data;//令p指示被删除的结点,q为p的双亲p = s;q = r;}if (p->left)//c指示取代p的子树{c = p->left;}else{c = p->right;}if (p==bt)//如果被删除的是根结点,则用c为新的根{bt = c;}else if (p==q->left)//否则c取代p{q->left = c;}else{q->right = c;}free(p);return true;
}

二、平衡二叉树

含有n个节点的二叉搜索树的平均查找长度和树的形态有关

平衡二叉树的定义

平衡二叉树(balanced binary tree)是带有平衡条件的二叉搜索树,又称为AVL树。平衡二叉树首先是二叉搜索树,其次满足以下条件:

(1)其根的左子树、右子树高度之差的绝对值不超过1

(2)其根的左子树与右子树也是二叉平衡树

给每个结点附加一个数字,为该结点左子树与右子树的高度差,这个数字被称为该结点的平衡因子。

平衡因子=结点左子树高度-结点右子树高度

平衡二叉树的平衡调整方法

将新元素在最小子树s上插入的类型分为以下四种:

LL型:新元素插入在s的左孩子的左子树上

LR型:新元素插入在s的左孩子的右子树上

RL型:新元素插入在s的右孩子的左子树上

RR型:新元素插入在s的右孩子的右子树上

调整原则:①降低高度②保持二叉搜索树的性质

首先计算插入后各个结点的平衡因子树,判断是哪一种类型,将数字大小进行排序,取中间值为根节点

对于相同的平衡因子数,选取最小的子树进行平衡

平衡二叉树平衡调整代码实现:

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
struct TreeNode
{int val;struct TreeNode* left;struct TreeNode* right;
};int maxDepth(struct TreeNode* root)
{if (root == NULL){return 0;}int leftdeep = maxDepth(root->left);int rightdeep = maxDepth(root->right);return leftdeep > rightdeep ? leftdeep + 1 : rightdeep + 1;
}
bool isBalanced(struct TreeNode* root)
{if (root == NULL){return true;}int leftdeep = maxDepth(root->left);int rightdeep = maxDepth(root->right);return abs(leftdeep - rightdeep) < 2 && isBalanced(root->left) && isBalanced(root->right);
}

平衡二叉树搜索树的缺陷

平衡二叉树搜索树的高度是logN,这个查找次数在内存中是很快的。但是当数据都在磁盘中时, 访问磁盘速度很慢,在数据量很大时,logN次的磁盘访问,是一个难以接受的结果。

三、m叉搜索树

m叉搜索树的定义

一颗m叉搜索树或者为空,或者满足如下性质:

(1)根节点最多有m课子树,并具有:n,P0,(K1,P1),(K2,P2),.....,(Kn,Pn)

其中Pi是指向子树的指针,0<=i<=n<m,Ki是元素的关键字值,1<=i<=n<m

(2)Ki<Ki+1,1<=i<n

(3)子树Pi上的所有关键字值都小于Ki+1,大于Ki,0<i<n

(4)子树Pn上所有的关键字值都大于Kn,子树P0上的所有关键字值都小于K1

(5)子树Pi也是m叉搜索树,0<=i<=n

图7.17是多叉搜索树结点的结构,图7.18给出了一棵四叉搜索树,图中的小方块代表空树。空树也称为失败结点,因为这是当搜索的关键字值k不在树中时到达的子树。失败结点不包含元素。图7.18中,根结点有两个孩子,树的结点中的关键字值有序排列,孩子数最多为4,所以称为四叉搜索树。

m叉搜索树的高度

高度为h的m叉搜索树中最多有(m^h)-1个元素

含有N个元素的m叉搜索树的高度在logm(N+1)到N之间,m为底数

四、B-树

B-树的定义

平衡搜索树基础上找优化空间:①压缩高度采用多叉树替代二叉树②一个结点里面有多个关键字及映射的值,在一个结点中存放多个元素而不是一个元素。

一棵m阶B-树是一棵m叉搜索树,它或者为空,或者满足如下性质:

(1)根结点至少有两个孩子

(2)每个分支结点都包含k-1个关键字和k个孩子,ceil(m/2)<=k<=m,ceil为向上取整函数

(3)每个叶子结点都包含k-1个关键字,其中ceil(m/2)<=k<=m

(4)所有的叶子结点都在同一层

(5)每个结点中给的关键字从小到大排列,结点当中k-1个元素正好是k个孩子包含的元素的值域划分

(6)每个结点的结构为:(n,A0,K1,A1,K2,A2,… ,Kn,An)其中,Ki(1≤i≤n)为关键字,且Ki<Ki+1(1<+i<=n-1)。Ai(0≤i≤n)为指向子树根结点的指针。且Ai所指子树所有结点中的关键字均小于Ki+1。n为结点中关键字的个数,满足ceil(m/2)-1≤n≤m-1。

一般情况下m会设置的比较大,

B-树的高度

一颗B-树所包含的元素总数是该B-树的空结点的总数减1。设B-树中空树的总是为s,那么一颗B-树的元素总数N=s-1

含有N个元素的m阶B-树的高度

B-树的搜索

B-树的搜索与二叉搜索树的搜索方式类似,B-树上进行搜索的过程是一个顺指针查找结点和在结点关键字中交叉进行的过程。

B-树的插入

①首先进行搜索操作,查找是否存在关键字值相同的元素。如果存在,则插入失败;如果不存在,搜索会停留在空树位置②执行插入操作,插入结点中。插入后需要判断关键字值数量与孩子结点数量,如果关键字值数量=m,则进行分裂操作。

从上面的图可以得到在m阶B-树中插入新元素的方法。
(1)搜索。在B-树中搜索给定关键字值的元素。如果搜索成功,表示有重复元素,则插入运算失败终止;否则转步骤(2)将新元素插入搜索失败结点上一层处的叶结点(设地址为q)。
(2)插入。将新元素和一个空指针插入结点q。如果插入后,结点q未上溢出,即结点包含的元素个数未超过m-1(指针数未超过m),则插入运算成功终止。否则转步骤(3)进行结点的分裂操作。
(3)分裂。以「m/27处的元素为分割点,将结点q一分为三:第1个位置至第m/2-1个位置的元素保留在原来的结点q中;第[m/2]+1个位置至第m个位置的元素存放在新创建的结点(设地址为q')中;而第「m/27个位置的分割点元素和新结点地址q'插入结点q的双亲结点。继续检查此双亲结点的上溢出问题。如果没有上溢出,则插入运算结束,否则重复执行步骤(3),继续该双亲结点的分裂操作,直至不再产生上溢出现象。
需要注意的是,如果按照(3)的原则,根结点产生分裂,由于根结点没有双亲,那么分裂产生的两个结点的指针以及分割点元素将组成一个新的根结点,从而导致B-树长高一层。

关于分裂:如果关键字的数量=m,则满,满则分裂出兄弟。同时将一半的关键字值与孩子结点分裂给兄弟。如果满了的结点有m个关键字,则分裂右边的m/2给兄弟,并将中位数提取给父亲结点。如果没有父亲结点就创建新的根。

B-树的删除

①首先进行搜索操作,查找元素是否存在②如果被删除的元素在叶结点上,则直接从该叶结点中删除该元素,并检查是否发生下溢出。如果没有下溢出,则删除运算结束,否则处理下溢出。③如果被删除的元素不在叶结点上,则首先执行替代操作,用该元素右子树上的最小元素取代它

如果该结点为最后一层的非终端结点,有下列 3 种可能,下面用3阶B-树举例:

  • 被删关键字所在结点中的关键字数目不小于⌈m/2⌉,则只需从该结点删除该关键字 Ki 以及相应的指针 Ai 。

  • 被删关键字所在结点中的关键字数目等于⌈m/2⌉-1,而与该结点相邻的右兄弟结点(或者左兄弟)结点中的关键字数目大于⌈m/2⌉-1,只需将该兄弟结点中的最小(或者最大)的关键字上移到双亲结点中,然后将双亲结点中小于(或者大于)且紧靠该上移关键字的关键字移动到被删关键字所在的结点中。

  • 被删除关键字所在的结点如果和其相邻的兄弟结点中的关键字数目都正好等于⌈m/2⌉-1,假设其有右兄弟结点,且其右兄弟结点是由双亲结点中的指针 Ai 所指,则需要在删除该关键字的同时,将剩余的关键字和指针连同双亲结点中的 Ki 一起合并到右兄弟结点中。

从上面的例子可以得到从B-树删除元素的步骤。
(1)搜索。在B-树中搜索给定关键字值的元素。如果搜索不成功,则删除运算失败终止;否则转(2)执行元素的删除操作。
(2)删除。首先判断删除类型,根据删除类型选用相应的删除方法。
如果属于情形1,即被删除的元素在叶结点中,则转(3)执行从叶结点中删除该元素的操作。如果属于情形2,即被删除的元素不在叶结点中,则用该元素右子树上的最小元素(必定在叶结点中)取代它,从而将问题转化为情形1,然后转(3)执行从叶结点中删除该替代元素的操作。
(3)从叶结点中删除元素。首先从叶结点中直接删除该元素,如果删除元素后没有下溢出(即当前结点包含m/2-1个元素),则删除运算成功终止。否则首先考虑采用“借”的方法处理下溢出(请注意:为避免删除结果不唯一,这里我们约定“借”操作遵循先左后右的顺序,即若左侧兄弟有富余,则从左侧兄弟“借”一个元素,否则,若右侧兄弟有富余,则向右侧兄弟“借”一个元素)。如果左右两侧兄弟结点都没有富余,则采用“并”的方法处理下溢出(同样,我们约定“并”操作遵循先左后右的顺序,即若当前结点有左侧兄弟,则将该结点与其左侧兄弟“并”成一个结点,否则与右侧兄弟“并”成一个结点),然后继续检查其双亲结点的下溢出问题。如果没有下溢出,则删除运算结束,否则继续该双亲结点的先“借”后“并”操作,直至不再有下溢出现象产生。
同样需要注意的是,如果“并”操作导致根结点中的一个元素被删除,并且该结点只包含一个元素,则根结点成为不包含任何元素的空结点,此时B-树将变矮一层。

数据结构基础--搜索树相关推荐

  1. python数据结构基础(单链表,多链表,二叉树)

    python数据结构基础(单链表,多链表,二叉树) 数据结构指数据对象中数据元素之间的关系 Python 给我们提供了很多现成的数据结构类型,这些系统自己定义好的,不需要我们自己去定义的数据结构叫做 ...

  2. 数据结构基础:P4.2-树(二)--->二叉平衡树

    本系列文章为浙江大学陈越.何钦铭数据结构学习笔记,前面的系列文章链接如下: 数据结构基础:P1-基本概念 数据结构基础:P2.1-线性结构->线性表 数据结构基础:P2.2-线性结构->堆 ...

  3. Algorithm:【Algorithm算法进阶之路】之数据结构基础知识

    Algorithm:[Algorithm算法进阶之路]之数据结构基础知识 相关文章 Algorithm:[Algorithm算法进阶之路]之数据结构二十多种算法演示 Algorithm:[Algori ...

  4. asp子窗口读取父窗口数据_算法与数据结构基础 - 数组(Array)

    数组基础 数组是最基础的数据结构,特点是O(1)时间读取任意下标元素,经常应用于排序(Sort).双指针(Two Pointers).二分查找(Binary Search).动态规划(DP)等算法.顺 ...

  5. 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority Queue)

    堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...

  6. 数据结构基础入门知识

    数据结构基础入门知识 ------ 数据结构:理解和练习 <异类-不一样的成功启示录> IP/26 192 IP/25 128 IP/24 192. 128 64  32 16  2  1 ...

  7. 翻译:程序员数据结构基础:选择正确的数据结构

    本文转载自GameDev.net,仅供学习交流.因为刚刚开始学习翻译,难免有些疏漏,如果有哪些地方翻译的不正确,请不吝告知,万分感谢. 原文链接:http://www.gamedev.net/page ...

  8. 在列表前方插入一个数据_通俗易懂的Redis数据结构基础教程

    Redis有5个基本数据结构,string.list.hash.set和zset.它们是日常开发中使用频率非常高应用最为广泛的数据结构,把这5个数据结构都吃透了,你就掌握了Redis应用知识的一半了. ...

  9. 由任意二叉树的前序遍历序列和中序遍历序列求二叉树的思想方法_算法与数据结构基础 - 二叉树(Binary Tree)...

    二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...

最新文章

  1. 新手必看,17 个常见的 Python 运行时错误
  2. c++ 实例精通(1)
  3. Python内置数据结构--列表
  4. OpenGL几何着色器
  5. php的autoload机制
  6. 前端学习(3234):react生命周期1
  7. vscode使用教程-开始学习前端开发啦~
  8. 利用bat文件批量修改文件后缀名
  9. 用户sa登录失败的解决办法
  10. 高通骁龙845与骁龙710处理器参数对比分析
  11. 【读书笔记】小狗钱钱
  12. 利用Python实现视频中人物的人脸转换超详细教程
  13. selenium简介,原理,优点,工作过程,定位方式
  14. java仿QQ微信聊天室
  15. Firefox 主页 被篡改为 2345主页
  16. open_nsfw: 雅虎基于Caffe的成人图片识别模型
  17. android usb rndis驱动,基于安卓的rndis驱动的usb网络共享的实现
  18. java二级考试备考_计算机二级java如何备考 方法在这里
  19. 优化神经网络训练的17种方法
  20. 基于JSP的珠宝商城的设计与实现

热门文章

  1. win10打开蓝牙_学会了这些win10快捷键,可以极大的提高你的工作效率
  2. 什么请假理由让领导无法拒绝—《职场规则》
  3. Django自定义Storage实现图片上传至各大OSS(上篇)
  4. 我走过最长的路,就是XMX的套路
  5. 董事长、总裁与CEO
  6. 用android写的微信闲聊机器人
  7. ARM基础与简单汇编
  8. Nature综述:鸟枪法宏基因组-从取样到数据分析——2万字带你系统入门宏基因组实验和分析
  9. shell脚本下载小猪佩奇中文版全集mp3
  10. 太极图正确画法_道教知识:太极图的正确画法