BST and Heap详解
BST(Binary Search Tree)
基本特点:
- 二叉树
- 集合中的数据具有可比较大小的关键码
- 数据之间满足BST特性
- 中序遍历可得到一个递增的数据序列(可作为判断一棵二叉树是否是BST的方法)
- 同一个数据集合,可存在多个不同形态的BST树
基本操作
- 问题描述+求解动机+算法思想+算法步骤+性能分析
- 进行操作,都需要:先找到要操作的数(位置),进行操作,保证BST的特性,保障优的算法性能。
| 查找(logn) | 插入(logn ~ n) |
|---|---|
| 若给定值小于根结点的关键字,则继续 在左子树上进行查找 | 若给定值小于根结点的关键字,则继续在左子树上进行插入; 将返回值(结点指针)设置为(当前)根结点的左孩子 |
| 若给定值大于根结点的关键字,则继续 在右子树上进行查找 | 若给定值大于根结点的关键字,则继续在右子树上进行插入; 将返回值(结点指针)设置为(当前)根结点的右孩子 |
| 若给定值等于根结点的关键字,则查找 成功 | / |
删除操作:
从最简单的情况开始——删除最小值;
由BST特性可知,BST中最小值一定在左子树的最左边取到,于是去get到它。
BST删除最小值算法思想(非递归)
1)定义一个指向BST树结点的临时指针变量p和pp。
2)从BST的根结点开始;将其值赋值给p。
3)若p的左孩子不等于空指针,将p的值赋值给pp;然后,将p的左孩子指针的值赋值给p;
4)重复第3步,直至p的左孩子的值等于空,结束查找。
5)p所指结点即为最小值结点。
6)若最小值结点不是根结点(BST根结点的左孩子不为空),则将p的右孩子指针作为pp的左孩子,否则将根结点的右孩子作为新的根结点。
算法分析
BST的最小值结点,从根结点的左孩子开始, 第一个左孩子为空的结点。
算法的步骤分为查找和接入。
- BST删除最小值的时间复杂度等于查找时间复杂度。
- 如果二叉树是平衡的,则有n个结点的二叉树 的高度约为logn,但是,如果二叉树完全不平衡(如成一个链表的形状),则其高度可以达到n。
BST删除算法思想
- 定位到待删除结点,此时有三种情况,设要删除结点为x,其父节点为px
1) 无子节点:直接将该 px->child 置空;
2) 有一个子节点:px->child = px->child->child;
3) 有两个子节点:找到X结点右子树中的最小值结点(后继结点)或者是左子树中的最大值(前驱节点),交换X结点的值和该节点的值(实际上实现了两个结点的互换),删除该结点,设置被删除结点的地址为该结点右子树的的最小值地址。 - 此时要注意的是,这里指的删除是指删除某一个值,而不是非要删除这个结点(用其他结点代替它被删除也行,反正值不会再出现在二叉树中了)。

# Heap
堆树或是一棵空树,或是具有下列性质的一棵**完全二叉树**(堆的局部有序特性):
- 每一个结点存储的值都小于等于其子节点存储的值——最小值堆(小顶堆)
- 每一个结点存储的值都大于等于其子节点存储的值——最大值堆(大顶堆)
由于是完全二叉树,用顺序表就十分方便存储
同一个数据集合,可以存在多个不同形态的堆
基本操作
插入:插入新值,新增结点,保持堆特性,算法代价要小
在哪最容易插入呢?叶子结点 最后的那个
算法思想:
均以最大值堆为例
- 在堆的末尾新增一个结点,存放新元素值val;
- 对其进行调整以维持堆的特性:
1.若它大于父节点的值,则将其与父节点交换,继续进行比较;
2.若它小于等于父节点的值或者是根节点了,则已处在正确位置,结束。
**算法分析**
- 堆的插入操作,插入结点都是作为一个叶子 结点插入到堆中。
- 算法的步骤分为插(接)入和交换值。
- 接入过程不需要移动结点,也不会整体改动树,所以时间开销为常数。
- 堆的插入时间复杂度取决于交换的次数。
- 堆是完全二叉树,则有n个结点的堆的高度为logn。插入新元素时,最佳情况时不用交换,最差情况交换logn,平均情况为logn。
构建
- 逐个插入法:新建一棵空堆,然后把数据集中的n个元素依次取出,逐个执行堆的 插入基本操作。
- 交换法建堆:新建一棵有n个结点的完全二叉树;然后把数据集中的n个元素任意的存储到完全二叉树中;判断完全二叉树中所有的父子结点对,若父结点中的元素值小于子结点中的元素值,则交换,直至完全二叉树满足堆的性质。
仅对第二种方法进行介绍。
交换法建堆算法思想
先要掌握调整结点的两种方法:向上调整and向下调整
- 向上调整(上拉):将要调整的结点x与其父节点进行比较,若比其大,则将这俩交换位置,再继续将x与现在的父节点进行比较;直至x为根节点或x小于等于其父节点的值为止。
- 向下调整(下拉):将要调整的结点x与其两个子节点进行比较,若小于它们,则取其中较大的那个与x交换,继续将x与其子节点进行比较;直至x大于它两个子节点的值或者为叶节点为止。
掌握了上面两种方法,那么建堆就容易了。
简述思路:
1. 直接将要存的数据按输入顺序存放到数组中;
2. **从[n/2]号位开始倒着枚举,对每个遍历到的结点i进行[i,n]范围内的调整**
为什么倒着枚举呢?
首先说为什么从[n/2]号位开始吧,因为其叶节点无法向下调整了,只需对所有非叶节点进行调整即可;
每一次调整都**将一个结点调整到了合适的位置,也就是说,以该节点为根结点的树已经满足了堆的特性,当前子树中权值最大的结点就会处在根节点的位置,这样当遍历到其父亲结点时,就可以直接使用这个结果**。
这种做法保证每个结点都是以其为根节点的子树中的权值最大的结点。
删除
同样,我们先讨论最简单的情况——删除堆中最大值。
由堆的性质可知,堆的最大值就是根节点,那么就删除第一个元素就可以了。那怎么删?
算法思想
- 在堆树中找到保存最大值的结点
1)最大值堆的最大值结点是最大值堆树的根结点。 - 删除最大值
2)保存最大值,交换堆的根结点的(最大)值与堆的最后一个 结点的值,堆的元素个数减一。 - 保持树的堆特性
若新树不空,对新的根结点的值R ,执行下拉(shiftdown)操作
3)R的值大于或等于其两个子结点,此时堆结构已经完成;
4)R的值小于某一个或全部两个子结点的值,此时R应与两个子结点中值较大的一个交换,若R仍然小于其新子结点的一个或两个。在这种情况下,只需要简单地继续这种将“R拉下来” 的过程,直至到达某一层使它大于它的子结点,或者它成为叶结点。
算法分析
- 删除最大值堆的最大值,即删除最大值堆的根结点。
- 如果基于数组实现最大值堆,最大值在数组 的基地址。
- 算法的步骤分为交换和维持堆性质。
- 堆删除最大值的时间复杂度等于对根结点执行一次下拉操作的时间复杂度。
- 堆是完全二叉树,则有n个结点的堆的高度为logn。下拉根结点时,最佳情况时不用交换,最差情况交换logn,平均情况为logn。
删除算法:
算法思想
- 在堆树中找到保存被删除的值结点
1)遍历最大值堆树,查找被删除的值,记住值的结点地址。 删除该值
2)交换堆的结点的值与堆的最后一个结点的值,堆的元素个数减一。 - 保持新树的堆特性
3)这个结点位置新的值,可能比父结点的值大,要向上交换,直到 其小于或等于其父结点的值,或达到根结点位置
4)然后这个值(新的位置)很可能比它的另一个子树的结点小,要执行下拉(shiftdown)操作:
4.1)R的值大于或等于其两个子结点,此时堆结构已经完成;
4.2)R的值小于某一个或全部两个子结点的值,此时R应与两个子 结点中值较大的一个交换,若R仍然小于其新子结点的一个或两个。在这种情况下,只需要简单地继续这种将“R拉下来”的过程,直至到达某一层使它大于它的子结点,或者它成为叶结点。
算法分析
- 删除最大值堆的某个值。
- 算法的步骤分为查找,交换和维持堆性质。
- 如果基于数组实现最大值堆,堆删除值的时间复杂度取决于查找,交换和维持堆性质的的时间复杂度。
- 堆是完全二叉树,则有n个结点的堆的高度为 logn。堆的查找操作时间复杂度为O(n),交换 的时间开销是常数,维持堆性质的上推和下拉操作的时间复杂度为O(logn),所以堆删除操作的时间复杂度为O(logn)
那么我们来思考一下,为什么插入的时候将元素放到末尾只需将元素向上调整,而删除的时候,交换待删的元素与最后一个,需要向上调整再向下调整呢?为什么多了一步?
其实仔细想想还是比较容易想到的。插入的时候,原有的堆就已经具有堆的特性,只是在最后加了一个元素需要调整。举个例子,就是说上面的所有父节点的值都大于两个子节点的值,一旦大于父节点,毫不犹豫需要上移且不可能需要再次下移。
而删除的时候,由于最后一个元素移到了中间某个位置,整个堆的特性都已经被破坏。举个例子,该值可能比它的子节点的值要小,如果仅向上调整的话会导致堆特性没法恢复。
堆排序
由堆的特性可知,堆顶元素是最大的,因此堆排序的直观思路就是取出堆顶元素,然后将堆的最后一个元素替换至堆顶,再进行依次针对堆顶元素的向下调整——如此重复直至堆中仅有一个元素未被遍历。
具体实现时,为了节省空间,可以倒着遍历数组。假设当前访问到i号为,那么将堆顶元素与i号为元素交换,接着在[1,i-1]范围内对堆顶元素进行一次向下调整即可。
让我们来对比一下BST和堆吧
| | BST | Heap |
|---|---|
| insert | logn |
| find | n |
| delete | logn |
| create | n |
| Traverse | n |
BST and Heap详解的更多相关文章
- JVM性能分析工具详解--MAT等
获得堆转储文件 巧妇难为无米之炊,我们首先需要获得一个堆转储文件.为了方便,本文采用的是 Sun JDK 6.通常来说,只要你设置了如下所示的 JVM 参数: -XX:+HeapDumpOnOutOf ...
- STL之heap与优先级队列Priority Queue详解
一.heap heap并不属于STL容器组件,它分为 max heap 和min heap,在缺省情况下,max-heap是优先队列(priority queue)的底层实现机制.而这个实现机制中的m ...
- Tomcat使用详解
Tomcat简介 官网:http://tomcat.apache.org/ Tomcat GitHub 地址:https://github.com/apache/tomcat Tomcat是Apach ...
- slf4j log4j logback关系详解和相关用法
slf4j log4j logback关系详解和相关用法 写java也有一段时间了,一直都有用slf4j log4j输出日志的习惯.但是始终都是抱着"拿来主义"的态度,复制粘贴下配 ...
- 数据结构图文解析之:二叉堆详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- centos7.2环境elasticsearch-5.0.1+kibana-5.0.1+zookeeper3.4.6+kafka_2.9.2-0.8.2.1部署详解
centos7.2环境elasticsearch-5.0.1+kibana-5.0.1+zookeeper3.4.6+kafka_2.9.2-0.8.2.1部署详解 环境准备: 操作系统:centos ...
- Chrome开发者工具详解(4)-Profiles面板
Chrome开发者工具详解(4)-Profiles面板 如果上篇中的Timeline面板所提供的信息不能满足你的要求,你可以使用Profiles面板,利用这个面板你可以追踪网页程序的内存泄漏问题,进一 ...
- JAVA中的GC机制详解
优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只 ...
- mysql配置文件my.cnf详解
原文地址:mysql配置文件my.cnf详解 作者:gron basedir = path 使用给定目录作为根目录(安装目录). character-sets-dir = path 给出存放着字符集的 ...
随机推荐
- 线性回归 - LinearRegression - 预测糖尿病 - 量化预测的质量
线性回归是分析一个变量与另外一个或多个变量(自变量)之间,关系强度的方法. 线性回归的标志,如名称所暗示的那样,即自变量与结果变量之间的关系是线性的,也就是说变量关系可以连城一条直线. 模型评估:量化 ...
- JS中由闭包引发内存泄露的深思
目录 一个存在内存泄露的闭包实例 什么是内存泄露 JS的垃圾回收机制 什么是闭包 什么原因导致了内存泄露 参考 1.一个存在内存泄露的闭包实例 var theThing = null; var rep ...
- ASP.NET Core MVC 如何获取请求的参数
一次HTTP请求,就是一次标准IO操作.请求是I,是输入:响应式O,是输出.任何web开发框架,其实都是在干这两件事: 接受请求并进行解析获取参数 根据参数进行渲染并输出响应内容 所以我们学习一个框架 ...
- P1666前缀单词
题目传送门点我传送 Ⅰ.字典树+树型DP 非常奇妙的一种解法 第一部分:构建树 先对来的单词读入,插入字典树 然后对于一颗字典树,其实是有很多无用边的,所以我们需要删去一些边 删去非单词节点和非单词节 ...
- P2380狗哥采矿(状态不易设计)
描述:https://www.luogu.com.cn/problem/P2380 首先分析一下,易知传送带一定是要么向上,要么向右.且一定摆满了整个矩阵. 所以我们设 f [ i ] [ j ]表示 ...
- 线段树 G - Mayor's posters 小技巧
G - Mayor's posters POJ - 2528 这个题目要倒着来写,从后面往前面贴,因为前面的有些会被后面的覆盖. 所以我们就判断这张海报的位置有没有完全被覆盖,如果完全被覆盖了就不能贴 ...
- STM32CubeMX 多通道 ADC DMA 配置 测试小程序
要点: 1.STM32F103C8T6单片机 2.ADC+DMA 多通道 重点是ADC+DMA配置,ADC+DMA配置如下 其他配置略略略略. 然后各位自行直看.ioc文件,生成代码后在while之前 ...
- search(10)- elastic4s-multi_match:多字段全文搜索
在全文搜索中我们常常会在多个字段中匹配同一个查询条件或者在不同的字段中匹配不同的条件.比如下面这个例子: GET /books/_search { "query": { " ...
- 2020年python开发微信小程序,公众号,手机购物商城APP
2020年最新的技术全栈,手机短信注册登陆等运用, 精准定位用户 支付宝支付 以及前后端从0到大神的全部精解 2020年最新的技术全栈,手机短信注册登陆等运用, 精准定位用户 支付宝支付 以及前后端从 ...
- Day_12【集合】扩展案例4_判断字符串每一个字符出现的次数
分析以下需求,并用代码实现 1.利用键盘录入,输入一个字符串 2.统计该字符串中各个字符的数量(提示:字符不用排序) 3.如: 用户输入字符串 "If~you-want~to~change- ...