二叉树

image

该图片来自于维基百科

在计算机科学中,二叉树(英语:Binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒。

以上是维基百科关于 二叉树 的定义,即:

  • 二叉树是树形结构的一种
  • 它限制了每个节点最多只有两个分支 左或右
  • 思想类似于二分查找 但是二叉树是一种链式结构
排序二叉树

image

该图片来自于维基百科

二叉查找树(英语:Binary Search Tree),也称为二叉搜索树、
有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),
是指一棵空树或者具有下列性质的二叉树:

1.  若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2.  若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3.  任意节点的左、右子树也分别为二叉查找树;
4.  没有键值相等的节点。

以上是维基百科关于 排序二叉树 的定义,其实说白了就一句话,即:

  • 排序二叉树的左节点一定小于右节点
  • 上条规则同样适用于子树
实现一个排序二叉树
class BinarySearchTree{
  constructor() {
    this.root = null // 初始化根节点root
  }

  createNode(val) { //  创建新节点
    let left = null; // 左节点
    let right = null // 右节点
    return {
      val,
      left,
      right
    }
  }
  
  // 插入操作
  insert(val) {
    let new_node = this.createNode(val); // 创建一个新节点
    if (!this.root) {
      // 如果当前二叉树的根节点为null 说明是个空树 直接插入
      this.root = new_node;
    } else {
      // 不是空树 继续判断需要插入到左边还是右边 
      this.insertNode(this.root, new_node);
    }
  }
  
  insertNode(node, new_node) {
    if (node.val > new_node.val) {
      // 比父节点小 需插入左边
      if (!node.left) {
        node.left = new_node;
      } else {
        // 递归操作 直到node.left 为null的时候插入节点node即可 
        this.insertNode(node.left, new_node);
      }
    } else {
      // 比父节点大 需插入右边
      if (!node.right) {
        node.right = new_node;
      } else {
      // 类比同上
        this.insertNode(node.right, new_node);
      }
    }
  }
}

ok 至此,便简单的实现了一个排序二叉树。类似于数组或者链表的方法一样,有了树之后就是要对它进行操作

遍历
  • 遍历的本质是访问树中的每个节点
  • 由于二叉排序树的特殊性 根据访问根节点的先后顺序不同 遍历顺序可分为

    • 先序遍历: 先访问根节点 再访问左节点 最后右节点
    • 中序遍历: 先访问左节点 再访问根节点 最后右节点
    • 后序遍历: 先访问左节点 再访问右节点 最后根节点
  • 通过递归实现遍历 通过回塑来控制节点的顺序

此处需要注意的是 顺序是根据访问 根节点 的先后顺序。
先访问根就是先序后访问根就是后序

class BinarySearchTree{
  constructor() {
    this.root = null // 初始化根节点root
  }
  // 省略以上代码...
  
  
  // 先序遍历
  preOrderTraverse(cb){ // cb 是回调函数 callback
    this.preOrderTraverseNode(this.root, cb)
  }
  preOrderTraverseNode(node, cb){
    if(node){ // 如果节点不为null 就继续遍历
        // 1. 先访问根节点 直接获取到根节点this.root
        cb(node.val); 
        // 2. 再访问左节点
        this.preOrderTraverseNode(node.left, cb) 
        // 3. 最后访问右节点
        this.preOrderTraverseNode(node.right, cb)
    }
  }
  
  
  // 中序遍历 理解同上
  inOrderTraverse(cb) {
    this.inOrderTraverseNode(this.root, cb);
  }
  inOrderTraverseNode(node, cb) {
    if (node !== null) {
      this.inOrderTraverseNode(node.left, cb);
      cb(node);
      this.inOrderTraverseNode(node.right.cb);
    }
  }

  // 后序遍历 理解同上
  postOrderTraverse(cb) {
    this.postOrderTraverseNode(this.root, cb);
  }
  postOrderTraverseNode(node, cb) {
    if (node !== null) {
      this.postOrderTraverseNode(node.left);
      this.postOrderTraverseNode(node.right);
      cb(node);
    }
  }
  
}
获取最大值

排序二叉树获取最大或最小值特别方便, 因为本身排序二叉树就是一个排好序的树。
排序二叉树的右子树一定大于左字树,所以只需直到最右边的子树即可满足条件。

class BinarySearchTree{
  constructor() {
    this.root = null // 初始化根节点root
  }
  // 省略以上代码...
 
 findMax(){
    let p = this.root
    if(p){
        // while循环终止条件: p为null 或者 p没有了right节点 
        while(p && p.right){
            p = p.right
        }
    }
    
    return p // 此时的p就是排序二叉树最大节点
 }
}
查找某个值是否存在

基于上述的排序二叉树的特点,查找某个值是否在该排序二叉树上也变得十分简单

class BinarySearchTree{
  constructor() {
    this.root = null // 初始化根节点root
  }
  // 省略以上代码...
   
   search(val){
    let p = this.root
    // 拿到根节点和val一并传入searchNode方法
    this.searchNode(p, val)
   }
   
   searchNode(node, val){
    if(!node){
      return false
    }
    
    if(node.val > val){
      // 该值比节点值小 去左子树去接着递归查找
      this.searchNode(node.left, val)
    }else if(node.val < val){
      // 该值比节点值大 去右子树去接着递归查找
      this.searchNode(node.left, val)
    }else{
      // 该值 等于 节点值 找到了
      return true
    }
   }
}

Funky_Tiger
443 声望33 粉丝

刷题,交流,offer,内推,涨薪,相亲,前端资源共享...