6.树(入门与进阶)

这篇博客深入介绍了二叉树的基本概念,包括树的定义、二叉查找树的创建、遍历方法以及最大深度问题。进阶部分探讨了平衡树,如2-3查找树和红黑树的定义、插入操作、性质以及实现细节。还对比分析了B-树和B+树在存储数据和磁盘文件应用中的特点。
摘要由CSDN通过智能技术生成

目录

入门

一、二叉树入门

1.1树的基本定义

1.2 树的相关术语 

1.3 二叉树的基本定义

1.4 二叉查找树的创建

1.4.1二叉树的结点类

1.4.2 二叉查找树API设计

 1.4.3 二叉查找树实现

1.4.4 二叉查找树其他便捷方法

1.4.4.1 查找二叉树中最小的键

1.4.4.2 查找二叉树中最大的键

1.5.1 前序遍历

1.5.2 中序遍历

1.6 二叉树的层序遍历

1.7 二叉树的最大深度问题

1.8 折纸问题

进阶

一、平衡树

1.1 2-3查找树

1.1.1 2-3查找树的定义

2-结点:

3-结点:

1.1.2 查找

​编辑1.1.3 插入

1.1.3.1 向2-结点中插入新键

 1.1.3.2 向一棵只含有一个3-结点的树中插入新键

 1.1.3.3 向一个父结点为2-结点的3-结点中插入新键

1.3.1.4 向一个父结点为3-结点的3-结点中插入新键 

 1.3.1.5 分解根结点

 1.3.4 2-3树的性质

1.3.5 2-3树的实现

1.2 红黑树

1.2.1 红黑树的定义

 1.2.2 红黑树结点API

1.2.3 平衡化

1.2.5 向底部的2-结点插入新键

1.2.6 颜色反转

 1.2.8 根结点的颜色总是黑色

1.2.9 向树底部的3-结点插入新键

1.2.10 红黑树的API设计

 1.2.11 红黑树的实现

二、B-树

1.1 B树的特性

2.2 B树存储数据

2.3 B树在磁盘文件中的应用

2.3.1 磁盘

2.3.2 磁盘IO

三、 B+树

2.1 B+树存储数据

2.2 B+树和B树的对比

3.3 B+树在数据库中的应用

3.3.1 未建立主键索引查询

3.3.2 建立主键索引查询

3.3.3 区间查询


入门

一、二叉树入门

之前我们实现的符号表中,不难看出,符号表的增删查操作,随着元素个数 N 的增多,其耗时也是线性增多的,时 间复杂度都是O(n), 为了提高运算效率,接下来我们学习树这种数据结构。

1.1树的基本定义

树是我们计算机中非常重要的一种数据结构,同时使用树这种数据结构,可以描述现实生活中的很多事物,例如家 谱、单位的组织架构、等等。
树是由 n n>=1 )个有限结点组成一个具有层次关系的集合。把它叫做 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
树具有以下特点:
1. 每个结点有零个或多个子结点;
2. 没有父结点的结点为根结点;
3. 每一个非根结点只有一个父结点;
4. 每个结点及其后代结点整体上可以看做是一棵树,称为当前结点的父结点的一个子树;

1.2 树的相关术语 

结点的度:
一个结点含有的子树的个数称为该结点的度;
叶结点:
度为 0 的结点称为叶结点,也可以叫做终端结点
分支结点:
度不为 0 的结点称为分支结点,也可以叫做非终端结点
结点的层次:
从根结点开始,根结点的层次为 1 ,根的直接后继层次为 2 ,以此类推
结点的层序编号:
将树中的结点,按照从上层到下层,同层从左到右的次序排成一个线性序列,把他们编成连续的自然数。
树的度:
树中所有结点的度的最大值
树的高度 ( 深度 )
树中结点的最大层次
森林:
m m>=0 )个互不相交的树的集合,将一颗非空树的根结点删去,树就变成一个森林;给森林增加一个统一的根结点,森林就变成一棵树

 

孩子结点:
一个结点的直接后继结点称为该结点的孩子结点
双亲结点 ( 父结点 )
一个结点的直接前驱称为该结点的双亲结点
兄弟结点:
同一双亲结点的孩子结点间互称兄弟结点

1.3 二叉树的基本定义

二叉树就是度不超过 2 的树 ( 每个结点最多有两个子结点 )

 

满二叉树:
一个二叉树,如果每一个层的结点树都达到最大值,则这个二叉树就是满二叉树。

 

完全二叉树:
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树

 

1.4 二叉查找树的创建

1.4.1二叉树的结点类

根据对图的观察,我们发现二叉树其实就是由一个一个的结点及其之间的关系组成的,按照面向对象的思想,我们
设计一个结点类来描述结点这个事物。
结点类 API 设计:

 代码实现:

private class Node<Key,Value>{
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}

1.4.2 二叉查找树API设计

 1.4.3 二叉查找树实现

插入方法 put 实现思想:
1. 如果当前树中没有任何一个结点,则直接把新结点当做根结点使用
2. 如果当前树不为空,则从根结点开始:
2.1 如果新结点的 key 小于当前结点的 key ,则继续找当前结点的左子结点;
2.2如果新结点的key 大于当前结点的 key ,则继续找当前结点的右子结点;
2.3 如果新结点的 key 等于当前结点的 key ,则树中已经存在这样的结点,替换该结点的 value 值即可。

 查询方法get实现思想:

从根节点开始:
1. 如果要查询的 key 小于当前结点的 key ,则继续找当前结点的左子结点;
2.如果要查询的key 大于当前结点的 key ,则继续找当前结点的右子结点;
3.如果要查询的key 等于当前结点的 key ,则树中返回当前结点的 value
删除方法 delete 实现思想:
1. 找到被删除结点;
2.找到被删除结点右子树中的最小结点minNode
3.删除右子树中的最小结点
4. 让被删除结点的左子树称为最小结点 minNode 的左子树,让被删除结点的右子树称为最小结点 minNode 的右子 树
5. 让被删除结点的父节点指向最小结点 minNode

                                                                                                                                                                      代码:

   

//二叉树代码
public class BinaryTree<Key extends Comparable<Key>, Value> {
//记录根结点
private Node root;
//记录树中元素的个数
private int N;
//获取树中元素的个数
public int size() {
return N;
}
//向树中添加元素key-value
public void put(Key key, Value value) {
root = put(root, key, value);
}
//向指定的树x中添加key-value,并返回添加元素后新的树
private Node put(Node x, Key key, Value value) {
if (x == null) {
//个数+1
N++;
return new Node(key, value, null, null);
}
int cmp = key.compareTo(x.key);
if (cmp > 0) {
//新结点的key大于当前结点的key,继续找当前结点的右子结点
x.right = put(x.right, key, value);
} else if (cmp < 0) {
//新结点的key小于当前结点的key,继续找当前结点的左子结点
x.left = put(x.left, key, value);
} else {
//新结点的key等于当前结点的key,把当前结点的value进行替换
x.value = value;
}
return x;
}
//查询树中指定key对应的value
public Value get(Key key) {
return get(root, key);
}
//从指定的树x中,查找key对应的值
public Value get(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp > 0) {
//如果要查询的key大于当前结点的key,则继续找当前结点的右子结点;
return get(x.right, key);
} else if (cmp < 0) {
//如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;
return get(x.left, key);
} else {
//如果要查询的key等于当前结点的key,则树中返回当前结点的value。
return x.value;
}
}
//删除树中key对应的value
public void delete(Key key) {
root = delete(root, key);
}
//删除指定树x中的key对应的value,并返回删除后的新树
public Node delete(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp > 0) {
//新结点的key大于当前结点的key,继续找当前结点的右子结点
x.right = delete(x.right, key);
} else if (cmp < 0) {
//新结点的key小于当前结点的key,继续找当前结点的左子结点
x.left = delete(x.left, key);
} else {
//新结点的key等于当前结点的key,当前x就是要删除的结点
//1.如果当前结点的右子树不存在,则直接返回当前结点的左子结点
if (x.right == null) {
return x.left;
}
//2.如果当前结点的左子树不存在,则直接返回当前结点的右子结点
if (x.left == null) {
return x.right;
}
//3.当前结点的左右子树都存在
//3.1找到右子树中最小的结点
Node minNode = x.right;
while (minNode.left != null) {
minNode = minNode.left;
}
//3.2删除右子树中最小的结点
Node n = x.right;
while (n.left != null) {
if (n.left.left == null) {
n.left = null;
} else {
n = n.left;
}
}
//3.3让被删除结点的左子树称为最小结点minNode的左子树,让被删除结点的右子树称为最小结点
minNode的右子树
minNode.left = x.left;
minNode.right = x.right;
//3.4让被删除结点的父节点指向最小结点minNode
x = minNode;
//个数-1
N--;
}
return x;
}
private class Node {
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
}
//测试代码
public class Test {
public static void main(String[] args) throws Exception {
BinaryTree<Integer, String> bt = new BinaryTree<>();
bt.put(4, "二哈");
bt.put(1, "张三");
bt.put(3, "李四");
bt.put(5, "王五");
System.out.println(bt.size());
bt.put(1,"老三");
System.out.println(bt.get(1));
System.out.println(bt.size());
bt.delete(1);
System.out.println(bt.size());
}
}

1.4.4 二叉查找树其他便捷方法

1.4.4.1 查找二叉树中最小的键

在某些情况下,我们需要查找出树中存储所有元素的键的最小值,比如我们的树中存储的是学生的排名和姓名数据,那么需要查找出排名最低是多少名?这里我们设计如下两个方法来完成:

     

//找出整个树中最小的键
public Key min(){
return min(root).key;
}
//找出指定树x中最小的键所在的结点
private Node min(Node x){
if (x.left!=null){
return min(x.left);
}else{
return x;
}
}

1.4.4.2 查找二叉树中最大的键

在某些情况下,我们需要查找出树中存储所有元素的键的最大值,比如比如我们的树中存储的是学生的成绩和学生 的姓名,那么需要查找出最高的分数是多少?这里我们同样设计两个方法来完成:

public Key max()                找出树中最大的键

public Node max(Node x)    找出指定树x中,最大键所在的结点

 

//找出整个树中最大的键
public Key max(){
return max(root).key;
}
//找出指定树x中最大键所在的结点
public Node max(Node x){
if (x.right!=null){
return max(x.right);
}else{
return x;
}
}

 1.5 二叉树的基础遍历

很多情况下,我们可能需要像遍历数组数组一样,遍历树,从而拿出树中存储的每一个元素,由于树状结构和线性 结构不一样,它没有办法从头开始依次向后遍历,所以存在如何遍历,也就是按照什么样的搜索路径 进行遍历的问题。

                                                                                                                                                                                                                      我们把树简单的画作上图中的样子,由一个根节点、一个左子树、一个右子树组成,那么按照根节点什么时候被访问我们可以把二叉树的遍历分为以下三种方式:

1.前序遍历;
先访问根结点,然后再访问左子树,最后访问右子树
2. 中序遍历;
先访问左子树,中间访问根节点,最后访问右子树
3.后序遍历;
先访问左子树,再访问右子树,最后访问根节点
如果我们分别对下面的树使用三种遍历方式进行遍历,得到的结果如下:

                                                 

1.5.1 前序遍历

我们在 4.4 中创建的树上,添加前序遍历的 API
public Queue<Key> preErgodic() :使用前序遍历,获取整个树中的所有键
private void preErgodic(Node x,Queue<Key> keys) :使用前序遍历,把指定树 x 中的所有键放入到 keys 队列中
实现过程中,我们通过前序遍历,把 , 把每个结点的键取出,放入到队列中返回即可。
实现步骤:
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怀化第一深情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值