查找树是一种数据结构,它支持多种动态集合操作,包括search, minimum, maximum, predecessor, successor, insert以及delete。他既可以用作字典,也可以用作优先队列。

二叉查找树上基本操作的执行时间和树的高度成正比。对一棵n个结点的完全二叉树来说,这些操作的最坏情况运行时间为Θ(lgn)。但是如果树是含有那个结点的线性链,则这些操作的最坏运行时间是Θ(n)。本章可以看到一棵随机构造的二叉查找树的期望高度为O(lgn)。

实际中,并不能总是保证二叉查找树是随机构造的,但有些二叉查找树的变形能保证各种基本操作的最坏情况性能。第十三章介绍的红黑树,其高度为O(lgn)。第十八章介绍B树,这种结构对维护随机访问的二级存储器上的数据库特别有效。

二叉查找树

二叉查找树是按二叉树结构来组织的,每个结点除了key域和卫星数据外,还包含left,right和p。结点的存储方案满足以下性质:如果结点y是结点x左子树种的一个结点,则key[y]<=key[x]。如果y是x右子树中的一个结点,则key[x]<=key[y]。

对二叉查找树进行中序遍历可以有序输出所有的结点关键字。

练习:

12.1.3给出二叉树的一个非递归的中序遍历算法。

(1)可以用一个栈数据结构来模拟递归过程中的栈变化过程,从而完成非递归形式的遍历算法;

(2)也可以不借助栈数据结构,中序遍历的过程中是由从根到叶、从父到子的down过和从子到父的up过程组成的。在非递归的情况下,在处理某个节点x时,关键是要分清下一步是要down还是up。其实这可以由上一个结点位置来判断,如果上一个结点是x的父节点且x是它的左儿子,则应该往左down,如果x是它的右儿子,则应该up。如果上一结点是x的左儿子,则应该往右down,如果是x的右儿子,则应该up。

OPTYPE {down, up}

INORDER_WITHOUTSTACK(T)

1         Node x = root[T]

2         Node last_node = nil

3         OPTYPE last_op = down

4         while (!over)

5           switch (last_op)

6             Case down:

7                If (left[x]) last_node=x, last_op=down, x=left[x]

8                Else output(x);

9                    if (right[x]) last_node=x, last_op=down, x=right[x]

10                Else if (x.p) last_node=x,last_op=up,x=x.p

11                    Else over=true

12         Case up:

13            If (last_node = left[x]) output(x)

14                               If (right[x]) last_node=x, last_op=down, x=right[x]

15                             Else if (x.p) last_node=x,last_op=up,x=x.p

16                                 Else over=true

17          Else if (x.p) last_node=x,last_op=up,x=x.p

18                                 Else over=true

查询二叉查找树

查找关键字k:

从树根开始比较key[x]和k,如果key[x]=k停止,如果key[x]>k则往左子树查找,否则往右子树查找,如此循环。

最大关键字元素和最小关键字元素:

最大关键字就是树的最右结点:沿树根往右子节点移动,直到NIL。

最小关键字元素就是树的最左结点:沿树根往左子结点移动,直到NIL。

前驱和后继:

某个节点x的前驱:如果x有左子,那么前驱是左子树的最大关键字;否则从下往上查看x的祖先结点,直到某个节点y满足性质:x出现在y的右子树种。如果y存在,则y就是x的前驱,否则x没有前驱。

某个节点x的后继:如果x有右子,则x的后继是右子树的最小关键字;否则从下往上查看x的祖先结点,直到某个结点y满足性质:x出现在y的左字数中,如果y存在,则y是x的后继,否则x没有后继。

查找前驱和后继的最坏运行时间为lgn。

但对x查找其后继(前驱)的k个结点的运行时间为lgn+k,以前驱为例,证明如下:

假设x的左子树有k1个结点,如果k<k1,则k1时间内就可以完成,假设k1<k,遍历这k1个结点需要k1时间,找到下一个前驱需要往根方向移动,不妨设移动的距离为h1。如此往复。假设期间产生了 k1,k2,k3..,h1,h2…这些数据。可知 k1+k2+k3…<=k,h1+h2+…<=树的高度。所以运行时间k1+k2+k3+…+h1+h2+…<=lgn+k。

实际上,如果对树的最小结点调用n-1次后继操作,等同于中序遍历二叉查找树。

插入删除结点

插入一个结点与查找一个关键字的过程是基本一致的。当查找过程到达nil时,这个位置就是插入的位置。

删除的过程要稍微复杂一点:

(1)       如果结点没有子节点,则直接删除。

(2)       如果结点只有左子树或右子树,则删除该节点,然后用该子树的根节点来取代该节点的位置。

(3)       如果该节点有左右子树,则将该节点的数据与其前驱或后继结点的数据进行交换,再删除该前驱或后继结点

随机构造的二叉查找树

二叉查找树的操作性能依赖于树的高度,为了避免坏的关键字顺序造成树的高度过高,可以采用随机化的手段来构造树。可以证明,随机构造的二叉查找树的期望高度为O(lgn)。

先定义三个随机变量:

Xn:n个结点的二叉查找树的高度;

Yn:指数高度Yn=2Xn;Yn = 2*max(Yi-1,Yn-i)。

Rn:根节点的在关键字中的统计顺序。

Zn,i:定义指示器随机变量Zn,i=I{Rn=i}; E[Zn,i] = 1/n。

推理过程如下:

利用恒等式:,用数学归纳得出,再利用jensen不等式:,可得E[Xn] = O(lgn)。

随机构造二叉查找树与随机化快速排序比较

在构造二叉树的时候,当选定某个关键字称为根节点之后,那么比这个关键字大的关键字都将出现在该节点的右字数,小的关键字都将出现在左子树。这个过程与快速排序选定中轴节点的过程及其相似。进一步,所有的结点都将与根节点进行比较,但左子树中的结点不会与右子树的结点比较,反之亦然。可以看出构造二叉查找树的过程与快速排序的过程惊人地相似,实际上如果将选定根节点和选定中轴结点相对应,那么两者整个过程中所进行的元素比较是完全相同的,只是顺序不一致而已。

可以分析二叉查找树的平均结点深度。每个结点的深度等于其在插入树的过程中的比较次数,所有的比较次数之和平均为nlgn,因此平均的结点深度为lgn。

思考题

基数树:

基数树数据结构,用来存储0,1位串,位置串并没有存储在节点中,一个结点只要标明从根到该节点的路径是否构成一个有效位串。当查找某关键字a=a0a1…ap,时,在深度为i的一个结点处,如果ai=0,则向左转,如果ai=1则向右转。下图的基数树存储了位串0,001,10, 100,1011.白色结点为有效位串的终结结点。

设S为一组不同的二进制构成的集合,各串的长度之和为n。说明如何利用基数树,在Θ(n)时间内将S按字典顺序排序。

分析:构造基数树的过程很显然,假设一个位串的长度为k,则只需要时间k就可以将串插入基数树中。然后进行中序遍历就可以,按序输出。

如果将基数树扩展,是否可以用来存储一般的字符串,并加快查找过程?

转载于:https://www.cnblogs.com/longhuihu/archive/2011/02/26/10423370.html

算法导论第十二章:二叉查找树相关推荐

  1. 算法导论第十二章总结以及课后题答案

    二叉搜索树 总结 1.查找二叉搜索树中最大关键字元素 TREE-MAXIMUM(x)while x.right!=NILx=x.rightreturn x 2.查找二叉搜索树中最小关键字元素 TREE ...

  2. 【Java数据结构与算法】第十二章 哈夫曼树和哈夫曼编码

    第十二章 哈夫曼树和哈夫曼编码 文章目录 第十二章 哈夫曼树和哈夫曼编码 一.哈夫曼树 1.基本术语 2.构建思路 3.代码实现 三.哈夫曼编码 1.引入 2.介绍 3.代码实现哈夫曼编码综合案例 一 ...

  3. 算法导论第十五章:动态规划(一)

    前言:最近一直在刷leetcode,虽然经常遇到动态规划的问题,但是常常还是留下没有知识的泪水 练习解答链接:https://ita.skanev.com/

  4. 数字图像处理:第十二章 小波变换

    第十二章 小波变换 目录 1         引言 2         连续小波变换 3         二进小波变换 3.1      Haar变换 4         离散小波变换 4.1     ...

  5. 程序员编程艺术第一 二十二章集锦与总结(教你如何编程)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 程序员编 ...

  6. 程序员编程艺术第一~二十二章集锦与总结(教你如何编程)

    程序员编程艺术第一~二十二章集锦与总结(教你如何编程) 作者:July.编程艺术室. 出处:http://blog.csdn.net/v_JULY_v . 题记 好久没更新博客了,虽只有一个月,但对我 ...

  7. stm32 文件系统dma大小_「正点原子NANO STM32F103开发板资料连载」第二十二章 DMA 实验...

    1)实验平台:[正点原子] NANO STM32F103 开发板 2)摘自<正点原子STM32 F1 开发指南(NANO 板-HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 ...

  8. 【译】 WebSocket 协议第十二章——使用其他规范中的WebSocket协议

    概述 本文为 WebSocket 协议的第十二章,本文翻译的主要内容为如何使用其他规范中的 WebSocket 协议. 使用其他规范中的WebSocket协议(协议正文) WebSocket协议旨在由 ...

  9. CoreJava 笔记总结-第十二章 并发-2

    文章目录 第十二章 并发 `synchronized`关键字 同步块 监视器概念 `volatile`字段 `final`变量 原子性 死锁 线程安全的集合 阻塞队列 映射条目的原子更新 对并发散列映 ...

最新文章

  1. android开发常用的设计模式,android开发设计模式之——单例模式详解
  2. hbase 单机 java api,HBase学习(一)hbase安装(单机模式)和javaapi客户端访问hbase例子...
  3. 刘志勇:微博短视频百万级高并发架构
  4. 如果编程语言是女孩子
  5. linux--切换ipython解释器到python3
  6. matlab对比r语言,R语言与matlab循环时间对比
  7. 2020计算机应用模拟题,2020年函授本科计算机应用基础课后作业、模拟题及答案...
  8. 大学计算机python基础课件,大学计算机python基础课件2015lecture03.pdf
  9. BS架构和CS架构的优缺点
  10. 在GEE平台提取Sentinel-1 SAR GRD的VV+VH波段
  11. java快速开发项目_GitHub - Johnnyzhoutq/X-SpringBoot: X-SpringBoot是一个轻量级的Java快速开发平台,能快速开发项目并交付【接私活利器】...
  12. 如何进行有效的计划管理?
  13. 图形化界面设计软件简要介绍
  14. 比 996 更可怕的是职场 PUA
  15. react 展开收起写法(手动展开收起 和 自动展开收起)
  16. 47、SimpleOrientationSensor
  17. android字体带下划线
  18. oschina使用pages
  19. public/private/protected的具体区别
  20. mysql group by date_format( stat_time, '%Y/%m/%d' ) 优化

热门文章

  1. 【2008】奥运门票4月15日开始预定 四种购买方式供选
  2. java pdfreader去除水印_(最新)JAVA使用Icepdf 5.0.6 pro的PDF转图片去掉水印的方法...
  3. 在线实时大数据平台Storm集成redis开发(分布锁)
  4. MapReduce基础开发之八HDFS文件CRUD操作
  5. vc设置ani动画光标
  6. 数据结构源码笔记(C语言):快速排序
  7. android遍历拼接字符串,写个批处理脚本帮忙干活---遍历amp;字符串处理
  8. sqlserver 安装_安装sqlserver
  9. Python 技术篇-在cmd命令提示行里模拟动态下载进度条实例演示,cmd清除日志、打印动态内容方法
  10. Python中使用多重继承