Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ function genAlgorithmDataStructures() {
"2022-08-27-hash-table.md",
"2022-09-03-heap.md",
"2022-09-14-trie.md",
"2022-09-18-tree.md",
]
}
]
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
290 changes: 290 additions & 0 deletions docs/md/algorithm/data-structures/2022-09-18-tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
---
title: 第8章:树(BST)
lock: need
---

# 《Java 数据结构和算法》第8章:树(BST)

作者:小傅哥
<br/>博客:[https://bugstack.cn](https://bugstack.cn)

> 沉淀、分享、成长,让自己和他人都能有所收获!😄

## 一、前言

`Binary Search Tree历史`

二叉搜索树算法是由包括 PF Windley、Andrew Donald Booth、Andrew Colin、Thomas N. Hibbard 在内的几位研究人员独立发现的。该算法归功于 Conway Berners-Lee 和 David Wheeler ,他们在 1960 年使用它在磁带中存储标记数据。 最早和流行的二叉搜索树算法之一是 Hibbard 算法。

## 二、二叉搜索树数据结构

二叉搜索树(Binary Search Tree),也称二叉查找树。如果你看见有序二叉树(Ordered Binary tree)、排序二叉树(Sorted Binary Tree)那么说的都是一个东西。

![](https://bugstack.cn/images/article/algorithm/tree-bst-01.png)

- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;

---

二叉搜索树在日常开发中使用的场景还是比较多的,例如基于组合模式实现的规则引擎,它就是一颗二叉搜索树。但类似这样的开发中用到的二叉树场景,都是基于配置生成,所以组合出来的节点也更加方便控制树高和平衡性。这与 Java API HashMap 中的红黑树这样为了解决插入节点后仍保持树的平衡性是有所不同的。

所以二叉搜索树也是一颗没有经过调衡的基础性数据结构,在一定概率上它完成有可能退化成链表,也就是从近似O(logn)的时间复杂度退化到O(n)。关于二叉搜索树的平衡解决方案,包括;AVL树、2-3树、红黑树等,小傅哥会在后续的章节继续实现。

## 三、二叉搜索树结构实现

二叉搜索树是整个树结构中最基本的树,同时也是树这个体系中实现起来最容易的数据结构。但之所以要使用基于二叉搜索树之上的其他树结构,主要是因为使用数据结构就是对数据的存放和读取。那么为了提高吞吐效率,则需要尽可能的平衡元素的排序,体现在树上则需要进行一些列操作,所以会有不同的结构树实现。

而实现二叉搜索树是最好的基础学习,了解基本的数据结构后才更容易扩展学习其他树结构。

- 源码地址:[https://github.com/fuzhengwei/java-algorithms](https://github.com/fuzhengwei/java-algorithms)
- 本章源码:[https://github.com/fuzhengwei/java-algorithms/tree/main/data-structures/src/main/java/stack](https://github.com/fuzhengwei/java-algorithms/tree/main/data-structures/src/main/java/tree)

### 1. 树枝定义

```java
public Integer value;
public Node parent;
public Node left;
public Node right;
```

- 用于组成一颗树的节点,则需要包括;值和与之关联的三角结构,一个父节点、两个孩子节点。如果是AVL树还需要树高,红黑树还需要染色标记。

### 2. 插入节点

```java
public Node insert(int e) {
if (null == root) {
root = new Node(e, null, null, null);
size++;
return root;
}

// 索引出待插入元素位置,也就是插入到哪个父元素下
Node parent = root;
Node search = root;
while (search != null && search.value != null) {
parent = search;
if (e < search.value) {
search = search.left;
} else {
search = search.right;
}
}

// 插入元素
Node newNode = new Node(e, parent, null, null);
if (parent.value > newNode.value) {
parent.left = newNode;
} else {
parent.right = newNode;
}
size++;
return newNode;
}
```

- 首先判断插入元素时候是否有树根,没有则会把当前节点创建出一颗树根来。
- 如果当前树是有树根的,则对插入元素与当前树进行一个节点遍历操作,找到元素可以插入的索引位置 parent(挂到这个父节点下)。也就是 search 搜索过程。
- 最后就是插入元素,通过给插入值创建一个 Node 节点,并绑定它的父元素,以及把新元素挂到索引到的 parent 节点下。

### 3. 索引节点

```java
public Node search(int e) {
Node node = root;
while (node != null && node.value != null && node.value != e) {
if (e < node.value) {
node = node.left;
} else {
node = node.right;
}
}
return node;
}
```

- 值查找的过程,就是对二叉搜索树的遍历,不断的循环节点,按照节点值的左右匹配,找出最终相当的值节点。

### 4. 删除节点

```java
public Node delete(int e) {
Node delNode = search(e);
if (null == delNode) return null;
return delete(delNode);
}

private Node delete(Node delNode) {
if (delNode == null) return null;
Node result = null;
if (delNode.left == null) {
result = transplant(delNode, delNode.right);
} else if (delNode.right == null) {
result = transplant(delNode, delNode.left);
} else {
// 因为删除的节点,有2个孩子节点,这个时候找到这条分支下,最左侧做小的节点。用它来替换删除的节点
Node miniNode = getMiniNode(delNode.right);
if (miniNode.parent != delNode) {
// 交换位置,用miniNode右节点,替换miniNode
transplant(miniNode, miniNode.right);
// 把miniNode 提升父节点,设置右子树并进行挂链。替代待删节点
miniNode.right = delNode.right;
miniNode.right.parent = miniNode;
}
// 交换位置,删除节点和miniNode 可打印测试观察;System.out.println(this);
transplant(delNode, miniNode);
// 把miniNode 提升到父节点,设置左子树并挂链
miniNode.left = delNode.left;
miniNode.left.parent = miniNode;
result = miniNode;
}
size--;
return result;
}
private Node getMinimum(Node node) {
while (node.left != null) {
node = node.left;
}
return node;
}

private Node transplant(Node delNode, Node addNode) {
if (delNode.parent == null) {
this.root = addNode;
}
// 判断删除元素是左/右节点
else if (delNode.parent.left == delNode) {
delNode.parent.left = addNode;
} else {
delNode.parent.right = addNode;
}
// 设置父节点
if (null != addNode) {
addNode.parent = delNode.parent;
}
return addNode;
}
```

#### 4.1 删除单节点

![](https://bugstack.cn/images/article/algorithm/tree-bst-02.png)

- 待删除节点14,判断此节点的父节点的孩子节点,哪个等于14,找出左右
- 把待删节点的右孩子节点,挂到删除节点的右节点
- 给待删节点的右孩子节点,设置上父节点

#### 4.2 删除双节点

![](https://bugstack.cn/images/article/algorithm/tree-bst-03.png)

- 待删除节点64,含有双子节点,则需要根据第一个右子节点查找最小左子节点。从89到72,如果有比72还小的左子节点,继续排查。
- 排查到节点72,将72这个准备替换待删元素的节点,与右子节点73进行位置交换,过程与 4.1 相同。使用交换函数 transplant
- 最后是进行节点72与待删节点64的交换过程,更换三角关系,父节点、左子节点、右子节点。

## 四、二叉搜索树功能测试

为了方便观察树结构的变化,这里小傅哥找了一些资料资料,一种是我们可以通过程序来打印(类似大家之前打印99乘法表,另外是使用线上的可视化图:[https://visualgo.net/zh/bst?slide=1](https://visualgo.net/zh/bst?slide=1))

### 1. 随机插入元素

```java
@Test
public void test_binary_search_tree() {
BinarySearchTree tree = new BinarySearchTree();
for (int i = 0; i < 10; i++) {
tree.insert(new Random().nextInt(100));
}
System.out.println(tree);
}
```

**测试结果**

```java
/----- 91
| \----- 78
/----- 74
| \----- 67
61
| /----- 51
\----- 40
| /----- 28
\----- 14
\----- 7

Process finished with exit code 0
```

- 因为你测试时的随机数不同,可能会出现很多不同结构的二叉搜索树,也可能是一个类似链表结构的退化树。

### 2. 插入并且删除

```java
@Test
public void test_insert_delete(){
BinarySearchTree tree = new BinarySearchTree();
tree.insert(32);
tree.insert(7);
tree.insert(64);
tree.insert(63);
tree.insert(89);
tree.insert(72);
tree.insert(94);
tree.insert(6);
tree.insert(14);
tree.insert(18);
tree.insert(73);
System.out.println(tree);

// 删除单节点,只有一个孩子的父节点
// tree.delete(14);

// 删除双节点,拥有二个孩子的父节点
tree.delete(64);
System.out.println(tree);
}
```

**测试结果**

```java
/----- 94
/----- 89
| | /----- 73
| \----- 72
/----- 64
| \----- 63
32
| /----- 18
| /----- 14
\----- 7
\----- 6

/----- 94
/----- 89
| \----- 73
/----- 72
| \----- 63
32
| /----- 18
| /----- 14
\----- 7
\----- 6


Process finished with exit code 0
```

- 这个案例就是 [4.2 删除双节点](#) 的案例,删除了节点64以后,节点72被提取上来使用。*读者伙伴也可以尝试删除其他节点测试验证*

## 五、常见面试题

- 二叉搜索树结构简述&变T的可能也让手写
- 二叉搜索树的插入、删除、索引的时间复杂度
- 二叉搜索树删除含有双子节点的元素过程叙述
- 二叉搜索树的节点都包括了哪些信息
- 为什么Java HashMap 中说过红黑树而不使用二叉搜索树
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 第5章:HTTP请求参数解析
pay: https://articles.zsxq.com/id_ediiw6xewa0u.html
pay: https://articles.zsxq.com/id_js30xt5kdbro.html
---

# 《API网关》第5章:HTTP请求参数解析
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: 第6章:引入执行器封装服务调用
pay: https://articles.zsxq.com/id_ediiw6xewa0u.html
pay: https://articles.zsxq.com/id_hejw18twmcm1.html
---

# 《API网关》第6章:引入执行器封装服务调用
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,4 +623,8 @@ public void test_SqlSessionFactory() throws IOException {

- 本章节关于反射工具类的实现中,使用了大量的 JDK 所提供的关于反射一些处理操作,也包括可以获取一个 Class 类中的属性、字段、方法的信息。那么再有了这些信息以后就可以按照功能流程进行解耦,把属性、反射、包装,都依次拆分出来,并按照设计原则,逐步包装让外接更少的知道内部的处理。
- 这里的反射也算是小天花板的使用级别了,封装的工具类方式,如果在我们也有类似的场景中,就可以直接拿来使用。因为整个工具类并没有太多的额外关联,直接拿来封装成一个工具包进行使用,处理平常的业务逻辑中组件化的部分,也是非常不错的。*技术迁移、学以致用、升职加薪*
- 由于整个工具包中涉及的类还是比较多的,大家在学习的过程中尽可能的验证和调试,以及对某个不清楚的方法进行单独开发和测试,这样才能滤清整个结构是如何实现的。*当你把这块的内容全部拿下,以后再遇到反射就是小意思了*
- 由于整个工具包中涉及的类还是比较多的,大家在学习的过程中尽可能的验证和调试,以及对某个不清楚的方法进行单独开发和测试,这样才能滤清整个结构是如何实现的。*当你把这块的内容全部拿下,以后再遇到反射就是小意思了*

## 八、优秀作业

- [完成mybatis自己实现的MetaObject反射工具类。这块包括:元对象、对象包装器、对象工厂、对象包装工厂以及Reflector反射器。 @杨杨得亿🙉](https://t.zsxq.com/0666qVFqj)
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ pay: https://articles.zsxq.com/id_w629m13v0hni.html

## 三、优秀作业

- [三级缓存处理循环依赖 @Rechie](https://t.zsxq.com/06jEynIE2)
- [三级缓存处理循环依赖 @Rechie](https://t.zsxq.com/06jEynIE2)
- [手撕Spring-解决循环依赖加属性类型转化 @Chin](https://t.zsxq.com/06AyBeiYN)
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ pay: https://articles.zsxq.com/id_d0cpbs31880x.html

其实实现到本章节,关于IOC、AOP在日常使用和面试中高频出现的技术点都该涵盖了。那么为了补全整个框架内容的结构,方便读者后续在阅读 Spring 时不至于对`类型转换`的知识体系陌生,这里再添加一些关于此类知识的实现。

类型转换也可以叫做数据转换,比如从String到Integer、从String到Date、从Dubbo到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。
类型转换也可以叫做数据转换,比如从String到Integer、从String到Date、从Dubbo到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。

## 三、优秀作业

- [类型转换机制设计 @Rechie](https://t.zsxq.com/06jiAub2n)
1 change: 1 addition & 0 deletions docs/md/zsxq/material/study-experience.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,4 @@ lock: no
- [请教傅哥转正答辩和晋升答辩都问些什么?该如何准备呢?](https://t.zsxq.com/05iiEyzbE)
- [小傅哥你好,今天被TL通知,准备年底的晋级答辩,我们晋级答辩的要求是入职满一年(我9月满一年)](https://t.zsxq.com/05Bm6EYVj)
- [小傅哥想请教一下关于答辩的问题,最近刚通知职级晋升答辩,第一次答辩没有啥经验,也不知道写啥](https://t.zsxq.com/05rjqZNB6)
- [上就要校招试用期转正答辩了,想请问下您在转正的这个阶段,leader较为看重的点在哪?](https://t.zsxq.com/06QzZJ6Yz)