傻子都能懂得二叉树-搜索二叉树-平衡二叉树AVL-红黑树RBT

(一)二叉树的先序-中序-后序遍历

核心:

先序遍历从顶部开始,中序遍历从最左部开始(建议补充未完全二叉树后,有节
点的最左边),后序遍历从最下部开始(如果并列,则从最左下开始遍历)
且先序遍历第一个绝对是根节点,后续遍历最后一个绝对是根节点
不伦是先,中,后,序遍历,只是根节点遍历的顺序不同,左节点始终是在右节
点之前遍历(可用于检查)

步骤:

  1. 根据遍历类型确定起点,先序(根节点),中序(最左节点),后序(最左下节
    点)
  2. 以起点作为基元的根节点(不存在的节点可理解为空节点),开始按照遍历类型规
    律进行遍历:先序(根-左-右),中序(左-根-右),后序(左-右-根)
  3. 分情况判断是否为叶子节点
    若为先序:写出该基元的根-左,判断左节点是否为叶子节点,如果不是,
    则以该节点为下一层基元的根节点,按照遍历类型规律书写(递归下去去找)。如果
    是,则把该基元的右节点写出。
    若为中序:写出该基元的左-根,判断右节点是否为叶子节点,如果不是,
    则以该节点为下一层基元的根节点,按照遍历类型规律书写(递归下去去找)。如果
    是,则把该基元的右节点写出。
    若为后序:写出该基元的左,判断基元中的右节点是否为叶子节点,如果不
    是,则以该节点为下一层基元的根节点,按照左-右-根的规律书写(递归下去找),
    如果是,则写出该节点和其根节点
  4. 对于已经是叶子节点后,向上回溯,按照遍历类型的规则进行遍历,遍历过的元素
    不可重复遍历
    在这里插入图片描述

使用递归方式遍历

节点定义

1 /**
2 * 树节点
3 */
4 public class TreeNode {
5
6 /**
7 * 节点名称
8 */
9 private String name;
10 /**
11 * 权值
12 */
13 private int weight;
14 /**
15 * 左子节点
16 */
17 private TreeNode leftChildNode;
18 /**
19 * 右子节点
20 */
21 private TreeNode rightChildNode;
22 //省略getter,setter构造

排序类

package com.zuikaku.utils;
2
3 import com.zuikaku.pojo.TreeNode;
4
5
6 import java.util.ArrayList;
7 import java.util.List;
8
9 /**
10 * 排序工具
11 */
12 public class TreeSortUtils {
13
14 public static List<TreeNode> treeNodeList=new ArrayList<TreeNode>();
15 /**
16 * 递归调用,先序排序
17 * @param node
18 * @return
19 */
20 public static void root_left_right_sort(TreeNode node){
21
22 //根‐左‐右
23 if(node!=null){
24 treeNodeList.add(node);
25 root_left_right_sort(node.getLeftChildNode());
26 root_left_right_sort(node.getRightChildNode());
27 }
28
29 }
30
31 /**
32 * 递归调用,中序排序
33 * @param node
34 */
35 public static void left_root_right_sort(TreeNode node){
36 //左‐根‐右
37 if(node!=null){
38 left_root_right_sort(node.getLeftChildNode());
39 treeNodeList.add(node);
40 left_root_right_sort(node.getRightChildNode());
41 }
42 }
43 public static void left_right_root_sort(TreeNode node){
44 //左‐右‐根
45 if(node!=null){
46 left_right_root_sort(node.getLeftChildNode());
47 left_right_root_sort(node.getRightChildNode());
48 treeNodeList.add(node);
49 }
50 }
51 }

测试类

package com.zuikaku.utils;
2
3 import com.zuikaku.pojo.TreeNode;
4
5
6 import java.util.ArrayList;
7 import java.util.List;
8
9 /**
10 * 排序工具
11 */
12 public class TreeSortUtils {
13
14 public static List<TreeNode> treeNodeList=new ArrayList<TreeNode>();
15 /**
16 * 递归调用,先序排序
17 * @param node
18 * @return
19 */
20 public static void root_left_right_sort(TreeNode node){
21
22 //根‐左‐右
23 if(node!=null){
24 treeNodeList.add(node);
25 root_left_right_sort(node.getLeftChildNode());
26 root_left_right_sort(node.getRightChildNode());
27 }
28
29 }
30
31 /**
32 * 递归调用,中序排序
33 * @param node
34 */
35 public static void left_root_right_sort(TreeNode node){
36 //左‐根‐右
37 if(node!=null){
38 left_root_right_sort(node.getLeftChildNode());
39 treeNodeList.add(node);
40 left_root_right_sort(node.getRightChildNode());
41 }
42 }
43 public static void left_right_root_sort(TreeNode node){
44 //左‐右‐根
45 if(node!=null){
46 left_right_root_sort(node.getLeftChildNode());
47 left_right_root_sort(node.getRightChildNode());
48 treeNodeList.add(node);
49 }
50 }
51 }

在这里插入图片描述

(二)二叉树种类定义

[1]二叉树的种类是随着不断优化而更加严格定义的

  1. 二叉查找树(二叉排序树,二叉搜索树):即本质是二叉树,其左子节点小于根节
    点,右子节点大于根节点,理想时间复杂度为o(logn),但当极端情况下,二叉查找
    树退化为链表结构后会使复杂度上升为o(n)级别
  2. 二叉平衡树AVL:在满足二叉查找树的条件下,避免了其极端情况下变为链式结
    构,其规定层节点的左右子节点高度相差不大于1。其可以通过二叉查找树,左旋,
    右旋变化到二叉平衡树。原变化时的二叉查找树分为LL型,LR型,和RR型。二叉树
    结构越平衡,则效率就越好。
  3. 红黑树:进一步优化,为了解决转变为二叉平衡树时左旋,右旋效率低下,进一步
    作出以下限定。
    (1)首先具备二叉查找树的特点。
    (2)叶子节点是黑色的空节点,不存数据。
    (3)没有相邻的红色节点,之间必然存在个黑节点隔开。
    (4)根节点是黑色的。
    (5)节点到其叶子节点包含的黑色节点数量是相同的。
    这样操作能保证复杂度为o(logn),其特点就是节点增删的时候不会破坏结构。但要从效率上讲,平衡二叉树的效率依然优于,红黑树。
    在这里插入图片描述
    在RBTree中其他性质:
  • 最长路径一定不会长于最短路径的两倍(无论其是否包含NIL)
  • 最长路径和最短路径所经过的黑色节点数量一定相同(无论其-是否包含NIL)
  • 红黑树的效率稍小于平衡二叉树,理想情况下可达到O(logN),但必然小于2O(logN)【因为最长路径小于两倍的最短路径】
  • 红黑树牺牲了一点性能但保证了3次旋转内必能平衡

[2]二叉树左旋右旋操作
目的:减少树的高度,提高遍历效率,使树更加平衡

(1)左旋

步骤:

  1. 确定以哪个目标节点旋转,确定该目标节点是否存在右子节点
  2. 以目标节点的右子节点为Y轴,该右子节点的右下方不变,左方逆时针旋转,仅考
    虑与该右子节点直接相连的节点及其他们的子节点,其余暂不考虑,将该右子节点的
    父节点(就是目标节点)移动到其左子节点且保留其原来目标节点的左子结构。
  3. 由于移动,原目标节点的右子节点的原左子丢失,此时目标节点的右指针为空,就
    把这个原目标节点的右子节点的左子节点,放在现在目标节点的右子节点上
  4. 原目标节点的父节点左指针现在指向改为原目标节点的右子节点,左旋完成
    核心:
    目标节点的右子节点不为空
    目标节点的右子节点的右下部分不会改变
    旋转后节点个数不会减少
    案例:T为目标节点,Y为其右子节点
    在这里插入图片描述

(2)右旋:操作类似,和左转反着来

步骤:

  1. 确定以哪个目标节点旋转,确定该目标节点是否存在左子节点
  2. 以目标节点的左子节点为Y轴,该左子节点的左下方不变,右方顺时针旋转,仅考
    虑与该左子节点直接相连的节点及其他们的子节点,其余暂不考虑,将该左子节点的
    父节点(就是目标节点)移动到其右子节点且保留其原来目标节点的右子结构。
  3. 由于移动,原目标节点的左子节点的原右子丢失,此时目标节点的左指针为空,就
    把这个原目标节点的左子节点的右子节点,放在现在目标节点的左子节点上
  4. 原目标节点的父节点右指针现在指向改为原目标节点的左子节点,右旋完成
    核心:
    目标节点的左子节点不为空
    左子节点的左下部分不会改变
    旋转后节点个数不会减少
    案例:T为目标节点,Y为其左子节点


    在这里插入图片描述

[3]平衡二叉树AVL的左旋右旋
目标:对于一个平衡二叉树,其基本条件为,二叉搜索树+左右子节点高度不超过1(最长路径-最短路径<=1),在对其插入和删除操作将会破坏平衡性,需要对其左转和右转维护其平衡性
对于AVL选择一共分为4个情况

  • LL:路径较长子树在根节点以左,且新增节点在较长子树的左子节点(无论作为其左右)
  • RR:路径较长子树在根节点以右,且新增节点在较长子树的右子节点(无论作为其左右)
  • LR:路径较长子树在根节点以左,但新增节点在较长子树的右子节点(在其左)
  • RL:路径较长子树在根节点以右,但新增节点在较长子树的左子节点(在其左)
    在这里插入图片描述
  • 对于LL型,需要对根节点进行右转
    在这里插入图片描述
  • 对于RR型,需要对根节点进行左转
    在这里插入图片描述
  • 对于RL型,先对插入节点的父节点所在基元的父节点进行右转,然后对根节点进行左转
    在这里插入图片描述
  • 对于LR型,先对插入节点的父节点所在基元的父节点进行左转,然后对根节点进行右转
    在这里插入图片描述

若有什么不对,欢迎指正,ε=ε=ε=( ̄▽ ̄)
【!!!】欢迎关注我的个人线上课堂https://www.zuikakuedu.cn,内含JavaWeb与Unity游戏开发实战教程,完全免费!,Csdn博客涉及的课程资料也在该网站上

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

最咔酷学院

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

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

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

打赏作者

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

抵扣说明:

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

余额充值