数据结构(六)树JS实现

数据结构(六)树JS实现

树的特点

1.相关术语

  • 节点的度(Degree):节点的子树个数个数
  • 树的度:树中所有节点里最大的度
  • 叶节点(Leaf):度数为0的节点
  • 父节点(Parent):有子树的节点,相对其子节点
  • 子节点(Child):有父节点的节点,相对其父节点
  • 兄弟节点(Sibling):具有相同父节点的子节点
  • 路径和路径长度:树从根节点到最远叶节点之间拥有的
  • 节点的层次(Level):根节点在1层,其他热议节点的层数是其父节点的层数加一
  • 树的深度(Depth):树中所有节点里最大层数就是这棵树的深度

2.优点

  • 树是数组和链表的折中方案,既有数组的查询速度,又有链表的插入和删除的速度

3.树的表示方式

  • 普通的表示方法

在这里插入图片描述

  • 左儿子-右兄弟表示法

在这里插入图片描述

二叉树

树中每个节点最多有2个的子节点

二叉树的五种形态

在这里插入图片描述

二叉树特性

在这里插入图片描述

完美二叉树

在这里插入图片描述

完全二叉树

在这里插入图片描述

二叉树的存储方式

  • 数组

    从上到下,从左到右

在这里插入图片描述

*链表

每个节点都包含左节点和右节点的引用

在这里插入图片描述

二叉搜索树 (Binary Search Tree)

性质
  • 非空左子树所有键值小于根节点的键值
  • 非空右子树所有键值大于根节点的键值
  • 左,右子树本身也是二叉搜索树
  • 相对较小的值总是保存在左节点上,相对较大的值总是保存在右节点上
  • 查询效率较高
插入
  • 插入操作可以分解成每个节点与传入节点键值进行比较,大于传入键值则向左子节点查询,反之向右查询直到节点键值为null,可以利用递归方式
 insert(key) {
          //1.创建一个node对象
          let newNode = new this.Node(key)
          //2.判断root是否为空
          if (this.root == null) {
            this.root = newNode
          } else {
            this.insertNode(this.root, newNode)
          }
        }

        insertNode(node, newNode) {
          if (node.key > newNode.key) {
            //向左插入
            if (node.left == null) {
              node.left = newNode
            } else {
              this.insertNode(node.left, newNode)
            }
          } else {
            //向右插入
            if (node.right == null) {
              node.right = newNode
            } else {
              this.insertNode(node.right, newNode)
            }
          }
        }
遍历
  • 先序遍历: 递归函数每次先处理节点键值,再查询左节点查询,最后查询右节点
 preOrder(handler) {
          this.preOrderNode(this.root, handler)
        }

        preOrderNode(node, handler) {
          if (node !== null) {
            handler(node.key)

            this.preOrderNode(node.left, handler)

            this.preOrderNode(node.right, handler)
          }
        }
  • 中序遍历:递归函数 = left-> node.key -> right
 middleOrder(handlder) {
          this.middleOrderNode(this.root, handlder)
        }

        middleOrderNode(node, handlder) {
          if (node !== null) {
            this.middleOrderNode(node.left, handlder)

            handlder(node.key)

            this.middleOrderNode(node.right, handlder)
          }
        }
  • 后序遍历:left-> right -> node.key

     postOrder(handlder) {
              this.postOrderNode(this.root, handlder)
            }
    
            postOrderNode(node, handlder) {
              if (node !== null) {
                this.postOrderNode(node.left, handlder)
    
                this.postOrderNode(node.right, handlder)
    
                handlder(node.key)
              }
            }
    
最值查找
  • 最大值
max(root = this.root) {
          //扩展根据传入节点查找其下最值,默认等于根节点,允许传入节点,返回相关数组
          // let root = this.root
          while (root.right !== null) {
            root = root.right
          }
          return [root, root.key] //0-> root,1->key
        }
  • 最小值
min(root = this.root) {
          // let root = this.root
          while (root.left !== null) {
            root = root.left
          }
          return [root, root.key] //0-> root,1->key
        }
删除

分三种情况

    1. 删除的是叶子节点
    1. 删除的节点有一个子节点
  • ​ 3.删除的节点拥有超过一个子节点
1.查找要删除的点
/*
*搜索函数根据传入的键值找出对应节点
*return 数组node[] 0-> parent, 1->child, 2->isLeftChild
*/
searchNode(key) {
          let node = [null, this.root, false] //0-> parent, 1->child, 2->isLeftChild
          let root = this.root
          while (node[1] !== null) {
            if (node[1].key > key) {
              node[0] = node[1]
              node[2] = true
              node[1] = node[1].left
            } else if (node[1].key < key) {
              node[0] = node[1]
              node[2] = false
              node[1] = node[1].right
            } else if (node[1].key == key) {
              return node
            }
          }
          return false
        }

let nodeArray = this.searchNode(key)
2.判断节点情况
remove(key) {
          //1.查询要删除的节点
          let nodeArray = this.searchNode(key)
          if (nodeArray == false) {
            return null
          } else if (nodeArray[1].left == null && nodeArray[1].right == null) {
            //1.删除叶子节点
            if (nodeArray[2] == true) {
              nodeArray[0].left = null
            } else {
              nodeArray[0].right = null
            }
          } else if (nodeArray[1].left == null || nodeArray[1].right == null) {
            //2.删除的节点只有一个子节点
            if (nodeArray[2] == true) {
              if (nodeArray[1].left == null) {
                nodeArray[0].left = nodeArray[1].right
              } else {
                nodeArray[0].left = nodeArray[1].left
              }
            } else {
              if (nodeArray[1].left == null) {
                nodeArray[0].right = nodeArray[1].right
              } else {
                nodeArray[0].right = nodeArray[1].left
              }
            }
          } else {
            //3.删除的节点有超过一个子节点,左子树找最大,右子树找最小 。默认查左子树

            let max = this.getBest(nodeArray[1].left, true)[1] //找出其最大的节点
            let maxNode = this.searchNode(max) //找出最大节点的父节点

            if (nodeArray[0] == null) {
              //更改节点指向的顺序很重要
              this.root = maxNode[1]
              maxNode[0].right = maxNode[1].left
              maxNode[1].right = nodeArray[1].right
              maxNode[1].left = nodeArray[1].left
              return true
            }

            if (nodeArray[2] == true) {
              //当要删除的节点属于左节点
              nodeArray[0].left = maxNode[1]
              maxNode[1].left =
                nodeArray[1].left.key == maxNode[1].key
                  ? null
                  : nodeArray[1].left //判断
              maxNode[1].right = nodeArray[1].right
              maxNode[0].right = maxNode[1].left
            } else {
              nodeArray[0].right = maxNode[1]
              maxNode[1].left =
                nodeArray[1].left.key == maxNode[1].key
                  ? null
                  : nodeArray[1].left
              maxNode[1].right = nodeArray[1].right
              maxNode[0].right = maxNode[1].left
            }
          }
        }

getBest(node, isLeft) {
    //根据节点返回最大或最小节点
          if (isLeft) {
            return this.max(node)
          } else {
            return this.min(node)
          }
        }

二叉平衡树

  • 需求:二叉搜索树在插入一系列键值连续的节点时,会造成二叉树左右分布不均匀和深度增加,使查询等操作的时间复杂度下降到O(N),
  • 解决方案
AVL树
红黑树

通过规定并保持一些性质保证二叉树的分布均匀

特点
  • 1.节点是红色或者黑色
  • 2.根节点是黑色
  • 3.每个叶子节点都是黑色(NIL)
  • 4.每个红色节点都有两个黑色子节点(从叶子节点到根的路径上不能拥有两个连续的红色节点)
  • 5.任一节点到其各个叶子节点之间的黑色节点的数目相同
  • 在根到所有叶子节点的路径中,最长路径的长度不能是最短路径的两倍
  • 根据性质4任意路径不能有连续的两个红色节点
  • 根据性质5所有路径都具有相同数目的黑色节点,推出特点6
在插入一个新的节点时,如何保持红黑树的特点

在这里插入图片描述

  • 变色 – 左旋转 – 右旋转
  • 旋转:

在这里插入图片描述

插入操作

设要插入的节点为N(node),父节点P,祖父节点G,叔叔节点U

  • 情况一:N位于树的根上没有父节点

    直接将那个红色换黑色即可

  • 情况二:N的父节点P是黑色

    不变

  • 情况三:P->红,U->红,G->黑

    红红黑 ->黑黑红

在这里插入图片描述

  • 情况四:P->红,U->黑,N为左孩子

    P->黑,G->红,以G为根右旋转

在这里插入图片描述

  • 情况五:P->红,U->黑,N为右孩子

    以P为根进行左旋转 -> N变黑色 ->G变红色 ->以G为根进行右旋转

在这里插入图片描述

实例

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值