小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
刚接触算法的时候,第一个搞的就是树,并且是经典的二叉树,接触之后就发觉这个东西还是很费脑子的,所以记下了如下笔记,今天分享下, 代码比较多,完整代码戳这
1、BST概念
1)什么是树?
一种数据的抽象模型,类似金字塔,职员架构,从上至下
包含,根节点、子节点(子树)、叶子节点
2)二叉搜索树
由一个根节点,两个子节点组成 - 左侧节点永远小于右侧节点
使用二叉搜索树的好处 -- 已知左侧值永远小于右侧至,所有搜索树中的值每次可以排除一半的数据。大大优化搜索速度
2、声明一个符合二叉树的类
function BinarySearchTree () {
// 节点构造函数,一个符合二叉树的节点函数
var Node = function ( key ) {
this.key = key;
this.left = null;
thei.right = null;
},
root = null; // 用于存储二叉树,函数开始时用作临时变量
}
3、向二叉树中插入一个值
// 类中函数 -
判断根节点是否为空,根节点为空则将用户传值转为节点作为根节点
根节点不为空则将节点树以及用户传值转化的用户节点传入类外函数处理
// 类外函数 - 插入值函数
var insertNode = function ( node, data ) {
// 判断用户节点data是否小于树节点,是则进入左侧树节点判断
// 左侧树节点是否为空,为空则将用户节点置入,否则递归再次判断
4、中序遍历、先序遍历、后序遍历
// 类中函数 - 传递root值,用户参数
将用户传值以及BST传递给类外辅助函数处理
// 类外函数
判断树节点是否为空,空则不处理
树节点非空值,则回调自身再次判断,直至判断所有节点
三种遍历的区别
中序遍历:从小到大访问所有节点
先序遍历:先访问节点本身,然后访问节点左侧值,后访问右侧值
后序遍历:先访问所有后代,在访问节点本身
5、搜索最大/最小值
// 类中函数 - 将BST传入类外函数,返回自类外函数返回的值
// 类外函数 -
判断根节点是否存在,不存在返回空
存在则循环判断是否根节点值及左侧值存在,直至循环终端找到最小数
最大值则判断右侧
6、搜索指定值
// 类中函数
将BST以及用户传值传递类外函数处理
// 类外函数
判断根节点是否存在,不存在返回空
存在则判断用户传值是否小于根节点,根据结果进入对应区域再次递归判断
直至找到与用户传值相等的节点值
7、移除指定节点
// 类中函数
将BST以及用户传值传递类外函数处理
// 类外函数
判断根节点是否为空,为空则返回空
判断用户传值是否大于小于根节点, true则回调自身直至找到与用户传值相等的节点值
若节点值两个子节点都为空则将改节点直接设置为空
若左侧为空则返回右侧节点继续处理,右侧亦然
若有两个子节点则找到右侧节点的左侧值设置为当前节点(被移除的节点位置)
新节点的右侧节点值设置为自身的右侧值与自身交由回调返回的结果
8、自平衡二叉搜索树
// 类中函数
将BST及用户传值给类中主处理,最终返回的值设置为BST
// 类中主函数
根节点为空则将用户参数传入类中转化节点对象返回作为根节点
如果用户参数小于根节点值则讲节点左侧及用户参数递归最终作为左侧值
如果左侧值不为空则判断是否需要平衡
判断用户参数是否小于左侧节点值
是则右单旋转,否则左双旋转
如果用户参数大于根节点判断与上相反
将最终节点树返回
// 类外辅助函数
自平衡因子 - 判断传值节点是否存在,存在则返回递归左侧及右侧+1中的最大值
左单旋转 - RR - 右侧节点存入临时变量,右侧子节点右侧等于临时变量的左节点,临时变量左节点等于传递进来的节点
右单旋转 - LL - 左侧节点存入临时变量,左侧子节点左侧等于临时变量右侧节点,
右双旋转 - LR - 先左单旋转左节点,在右单旋转节点
左单旋转 - RL - 先右单旋转右节点,在左单旋转节点