算法—二叉查找树的相关一些操作及总结

二叉查找树得以广泛应用的一个重要原因就是它能够保持键的有序性,因此它可以作为实现有序符号表API中的众多方法的基础。这使得符号表的用例不仅能够通过键还能通过键的相对顺序来访问键值对。下面,我们要研究有序符号表API中各个方法的实现。

1.最大键和最小键

如果根结点的左链接为空,那么一棵二叉查找树中最小的键就是根结点;如果左链接非空,那么树中的最小键就是左子树中的最小键。简单的循环也能等价实现这段描述,但为了保持一致性我们使用了递归。找出最大键的方法也是类似的,只是变为查找右子树而已。

2.向上取整和向下取整

如果给定的键key小于二叉查找树的根结点的键,那么小于等于key的最大键(floor)一定在根结点的左子树中;如果给定的键key大于二叉查找树的根结点,那么只有当根结点右子树中存在小于等于key的结点时,小于等于key的最大键才会出现在右子树中,否则根结点就是小于等于key的最大键。这段描述说明了floor()方法的递归实现,同时也递推地证明了它能够计算出预期的结果。将“左”变为“右”(同时将小于变为大于)就能够得到ceiling()的算法。向上取整函数的计算如下图所示。

3.选择操作

二叉查找树中的选择操作和之前我们学习过的基于切分的数组选择操作类似。我们在二叉查找树的每个结点中维护的子树结点计数器变量N就是用来支持此操作的。

4.select()方法

5.删除最大键和删除最小键

二叉查找树中最难实现的方法就是delete()方法,即从符号表中删除一个键值对。作为热身运动,我们先考虑deleteMin()方法(删除最小键所对应的键值对),如下图所示,和put()一样,我们的递归方法接受一个指向结点的链接,并返回一个指向结点的链接。这样我们就能够方便地改变树的结构,将返回的链接赋给作为参数的链接。对于deleteMin(),我们要不断深入根结点的左子树中直至遇见一个空链接,然后将指向该结点的链接指向该结点的右子树(只需要在递归调用中返回它的右链接即可)。此时已经没有任何链接指向要被删除的结点,因此它会被垃圾收集器清理掉。我们给出的标准递归代码在删除结点后会正确地设置它的父结点的链接并更新它到根结点的路径上的所有结点的计数器的值。deleteMax()方法的实现和deleteMin()完全类似。

6.删除操作

我们可以用类似的方式删除任意只有一个子结点(或者没有子结点)的结点,但应该怎样删除一个拥有两个子结点的结点呢?删除之后我们要处理两棵子树,但被删除结点的父结点只有一条空出来的链接。第一个方法,在删除结点x后用它的后继结点填补它的位置。因为x有一个右子结点,因此它的后继结点就是其右子树中的最小结点。这样的替换仍然能够保证树的有序性,因为x.key和它的后继结点的键之间不存在其他的键。我们能够用4个简单的步骤完成将x替换为它的后继结点的任务(具体过程如下图所示):

Ο将指向即将被删除的结点的链接保存为t;

Ο将x指向它的后继结点min(t.right);

Ο将x的右链接(原本指向一棵所有结点都大于x.key的二叉查找树)指向deleteMin(t.right),也就是在删除后所有结点仍然都大于x.key的子二叉查找树。

Ο将x的左链接(本为空)设为t.left(其下所有的键都小于被删除的结点和它的后继结点)。

7.范围查找

为了确保以给定结点为根的子树中所有在指定范围之内的键加入队列,我们会(递归地)查找根结点的左子树,然后查找根结点,然后(递归地)查找根结点的右子树。

8.性能分析

二叉查找树中和有序性相关的操作的效率如何?要研究这个问题,我们首先要知道树的高度(即树中任意结点的最大深度)。给定一棵树,树的高度决定了所有操作在最坏情况下的性能(范围查找除外,因为它的额外成本和返回的键的数量成正比)。

命题:在一棵二叉查找树中,所有操作在最坏情况下所需的时间都和树的高度成正比。

证明:树的所有操作都沿着树的一条或两条路径行进。根据定义,路径的长度不可能大于树的高度。

9.总结

在某些场景中二叉查找树在最坏情况下的恶劣性能仍然是不可接受的。二叉查找树的基本实现的良好性能依赖于其中的键的分布足够随机以消除长路径。对于快速排序,我们可以先将数组打乱;而对于符号表的API,我们无能为力,因为符号表的用例控制着各种操作的先后顺序。

时间: 2024-08-13 12:16:18

算法—二叉查找树的相关一些操作及总结的相关文章

uva 12299 线段树 点相关的操作模板

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=502&page=show_problem&problem=3720 唯一值得一说的就是shift,变成更新点就行 这道题主要是测试下我做的算法模板 先贴模板 /**************************************************************** 2014.4 By Pilgr

(笔记)Linux内核中内存相关的操作函数

linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) 内核空间申请指定大小的内存区域,返回内核空间虚拟地址.在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间.有关buddy和slab,请参见<linux内核之内存管理.doc> gfp_t flags 的选项

拷贝构造,深度拷贝,关于delete和default相关的操作,explicit,类赋初值,构造函数和析构函数,成员函数和内联函数,关于内存存储,默认参数,静态函数和普通函数,const函数,友元

 1.拷贝构造 //拷贝构造的规则,有两种方式实现初始化. //1.一个是通过在后面:a(x),b(y)的方式实现初始化. //2.第二种初始化的方式是直接在构造方法里面实现初始化. 案例如下: #include<iostream> //如果声明已经定义,边不会生成 class classA { private: int a; int b; public: //拷贝构造的规则,有两种方式实现初始化 //1.一个是通过在后面:a(x),b(y)的方式实现初始化 //2.第二种初始化的方式是直

相关元素操作

五.相关元素操作: var a = document.getElementById("id");找到a: var b = a.nextSibling,找a的下一个同辈元素,注意包含空格:(弟) var b = a.previousSibling,找a的上一个同辈元素,注意包含空格:(兄) var b = a.parentNode,找a的上一级父级元素:(父) var b = a.childNodes,找出来的是数组,找a的下一级子元素:(子) var b = a.firstChild,

iOS与日期相关的操作

// Do any additional setup after loading the view, typically from a nib. //得到当前的日期 注意week1是星期天 NSDate *date = [NSDate date]; NSLog(@"date:%@",date); //得到(24 * 60 * 60)即24小时之前的日期,dateWithTimeIntervalSinceNow: NSDate *yesterday = [NSDate dateWithT

Go语言学习之os包中文件相关的操作(The way to go)

生命不止,继续 go go go !!! 今天跟大家分享学习的是os package,主要是介绍一些跟文件或文件夹相关的操作. os包 Package os provides a platform-independent interface to operating system functionality. The design is Unix-like, although the error handling is Go-like; failing calls return values o

服务相关的操作

二十.服务相关的操作127.0.0.1:6379[2]> DBSIZE    #查看一个库的键值数(integer) 1127.0.0.1:6379[2]> select 0OK127.0.0.1:6379> DBSIZE (integer) 10 127.0.0.1:6379> info   #查看redis服务信息# Serverredis_version:2.8.21redis_git_sha1:00000000略 127.0.0.1:6379> flushdb   

git commit 相关的操作

本地仓库状态相关的操作 1.  git  commit 将从上次提交后到现在这段时间内,暂存区所有的变化提交到版本库中: git  commit  -m '此次提交操作的简要说明(单引号括起来)' commit成功后会显示提交的文件: 2.  git  reset  --hard 撤销上次提交中的所有操作: 原文地址:https://www.cnblogs.com/virgosnail/p/10204693.html

R语言文件相关的操作

1. 文件系统介绍 R语言对文件系统的操作,包括文件操作和目录操作,函数API都定义在base包中. 2. 目录操作 2.1 查看目录 查看当前目录下的子目录. # 启动R程序 ~ R # 当前的目录 > getwd() [1] "/home/conan/R/fs" # 查看当前目录的子目录 > list.dirs() [1] "." "./tmp" 查看当前目录的子目录和文件. > dir() [1] "readme