【js】-【树、二叉树、二叉搜索树】-学习笔记

28 篇文章 0 订阅
14 篇文章 0 订阅

声明:本笔记是根据掘金小册总结的,如果要学习更多细节,请移步https://juejin.cn/book/6844733800300150797

1 二叉树递归

在 JS 中,二叉树使用对象来定义。它的结构分为三块:
数据域
左侧子结点(左子树根结点)的引用
右侧子结点(右子树根结点)的引用在这里插入图片描述

  1. 定义二叉树构造函数
// 二叉树结点的构造函数
function TreeNode(val) {
    this.val = val;
    this.left = this.right = null;
}
  1. 新建一个二叉树结点
const node  = new TreeNode(1)

1.1 先序遍历

按格式输出:

输入:
{1,#,2,3}
返回值:
[1,2,3]
// 所有遍历函数的入参都是树的根结点对象
function preorderTraversal( root ) {
    // write code here
    
    let res=[];
    function pro(root){
        if(!root ) return;
        res.push(root.val) ;
        pro(root.left);
        pro(root.right);
    }
   pro(root);
   return res; 
}

1.2 中序遍历

# 所有遍历函数的入参都是树的根结点对象
function inorder(root) {
    // 递归边界,root 为空
    if(!root) {
        return 
    }
     
    // 递归遍历左子树 
    inorder(root.left)  
    // 输出当前遍历的结点值
    console.log('当前遍历的结点值是:', root.val)  
    // 递归遍历右子树  
    inorder(root.right)
}

1.3 后序遍历

function postorder(root) {
    // 递归边界,root 为空
    if(!root) {
        return 
    }
     
    // 递归遍历左子树 
    postorder(root.left)  
    // 递归遍历右子树  
    postorder(root.right)
    // 输出当前遍历的结点值
    console.log('当前遍历的结点值是:', root.val)  
}

2 二叉树迭代

2.1 先序遍历

  1. 将根结点入栈
  2. 取出栈顶结点,将结点值 push 进结果数组
  3. 若栈顶结点有右孩子,则将右孩子入栈
  4. 若栈顶结点有左孩子,则将左孩子入栈
const preorderTraversal = function(root) {
  // 定义结果数组
  const res = []  
  // 处理边界条件
  if(!root) {
      return res
  }
  // 初始化栈结构
  const stack = [] 
  #1 首先将根结点入栈
  stack.push(root)  
  // 若栈不为空,则重复出栈、入栈操作
  while(stack.length) {
      # 2 将栈顶结点记为当前结点
      const cur = stack.pop() 
      # 3 当前结点就是当前子树的根结点,把这个结点放在结果数组的尾部
      res.push(cur.val)
      # 4 若当前子树根结点有右孩子,则将右孩子入栈,若当前子树根结点有左孩子,则将左孩子入栈
      if(cur.right) {
          stack.push(cur.right)
      }
      if(cur.left) {
          stack.push(cur.left)
      }
  }
  // 返回结果数组
  return res
};

2.2 后序遍历

左 -> 右 -> 根

const postorderTraversal = function(root) {
  // 定义结果数组
  const res = []  
  // 处理边界条件
  if(!root) {
      return res
  }
  // 初始化栈结构
  const stack = [] 
  // 首先将根结点入栈
  stack.push(root)  
  // 若栈不为空,则重复出栈、入栈操作
  while(stack.length) {
      // 将栈顶结点记为当前结点
      const cur = stack.pop() 
      # 当前结点就是当前子树的根结点,把这个结点放在结果数组的头部
      res.unshift(cur.val)
      // 若当前子树根结点有左孩子,则将左孩子入栈
      if(cur.left) {
        stack.push(cur.left)
      }  
      // 若当前子树根结点有右孩子,则将右孩子入栈
      if(cur.right) {
        stack.push(cur.right)
      }
  }
  // 返回结果数组
  return res
};

2.3 中序遍历

const inorderTraversal = function(root) {
  // 定义结果数组
  const res = []  
  // 初始化栈结构
  const stack = []   
  // 用一个 cur 结点充当游标
  let cur = root  
  # 当 cur 不为空、或者 stack 不为空时,重复以下逻辑
  while(cur || stack.length) {
      // 这个 while 的作用是把寻找最左叶子结点的过程中,途径的所有结点都记录下来 
      while(cur) {
          # 将途径的结点入栈
          stack.push(cur)  
          # 继续搜索当前结点的左孩子
          cur = cur.left  
      }
      # 取出栈顶元素
      cur = stack.pop()  
      // 将栈顶元素记录
      res.push(cur.val)  
      // 尝试读取 cur 结点的右孩子
      cur = cur.right
  }
  // 返回结果数组
  return res
};

3 二叉搜索树

3.1 查找数据域为某一特定值的结点

function search(root, n) {
    // 若 root 为空,查找失败,直接返回
    if(!root) {
        return 
    }
    // 找到目标结点,输出结点对象
    if(root.val === n) {
        console.log('目标结点是:', root)
    } else if(root.val > n) {
        // 当前结点数据域大于n,向左查找
        search(root.left, n)
    } else {
        // 当前结点数据域小于n,向右查找
        search(root.right, n)
    }
}

3.2 插入新结点

function insertIntoBST(root, n) {
    // 若 root 为空,说明当前是一个可以插入的空位
    if(!root) { 
        // 用一个值为n的结点占据这个空位
        root = new TreeNode(n)
        return root
    }
    
    if(root.val > n) {
        // 当前结点数据域大于n,向左查找
        root.left = insertIntoBST(root.left, n)
    } else {
        // 当前结点数据域小于n,向右查找
        root.right = insertIntoBST(root.right, n)
    }

    // 返回插入后二叉搜索树的根结点
    return root
}

3.3 删除指定结点

var deleteNode = function(root, key) {
    if(!root)  return  null;
    if(root.val >key){
        root.left = deleteNode(root.left, key)
    }else if(root.val < key){
        root.right = deleteNode(root.right, key)
    }else{ 
    # 1. 找到结点了
        if(!root.left) return root.right; 
        # 2.没有左结点,直接返回右边的
        if(!root.right) return root.left;
        # 3.左右结点都有,
        var node = root.right;
        while(node.left) node=node.left; 
        # 4.寻找欲删除节点右子树的最左节点
        node.left =root.left; 
        # 5.将欲删除节点的左子树成为其右子树的最左节点的左子树
        return root.right; //欲删除节点的右子顶替其位置,节点被删除
    }
    return root;
};

3.4 二叉搜索树的验证

检验每棵子树中是否都满足 左 < 根 < 右 这样的关系

const isValidBST = function(root) {
  // 定义递归函数
  function dfs(root, minValue, maxValue) {
      // 若是空树,则合法
      if(!root) {
          return true
      }
      // 若右孩子不大于根结点值,或者左孩子不小于根结点值,则不合法
      if(root.val <= minValue || root.val >= maxValue) return false
      // 左右子树必须都符合二叉搜索树的数据域大小关系
      return dfs(root.left, minValue,root.val) && dfs(root.right, root.val, maxValue)
  }
  // 初始化最小值和最大值为极小或极大
  return dfs(root, -Infinity, Infinity)
};

3.5 将排序数组转化为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

var sortedArrayToBST = function(nums) {
    if(!nums.length) return null;
    const middle= Math.floor(nums.length/2);
    let root = new TreeNode(nums[middle]);
    root.left= sortedArrayToBST(nums.slice(0,middle));
    root.right = sortedArrayToBST(nums.slice(middle+1));
    return root;
};

3.6 其他搜索树的模板

void BST(TreeNode root, int target) {
    if (root.val == target)
        // 找到目标,做点什么
    if (root.val < target) 
        BST(root.right, target);
    if (root.val > target)
        BST(root.left, target);
}

4 平衡二叉树

4.1 判断它是否是高度平衡的二叉树

一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

var isBalanced = function(root) {
    if(!root) return true;
    return Math.abs(height(root.left)-height(root.right))<=1&&isBalanced(root.left)&&isBalanced(root.right);
};
var height = function(root)
{
    if (!root)
        return 0;
    else
        return Math.max(height(root.left), height(root.right)) + 1;
}

5. 对称性递归

5.1 相同的树

var isSameTree = function(p, q) {
    if(!p&&!q) return true;
    else if(!p||!q) return false;
    return p&&q &&(p.val===q.val)&& isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
};

5.2 二叉树的最大深度

var maxDepth = function(root) {
    if(!root) return 0;
    return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
};

5.3 翻转二叉树

const invertTree = function(root) {
    // 定义递归边界
    if(!root) {
        return root;
    }
    // 递归交换右孩子的子结点
    let right = invertTree(root.right);
    // 递归交换左孩子的子结点
    let left = invertTree(root.left);
    // 交换当前遍历到的两个左右孩子结点
    root.left = right;
    root.right = left;
    return root;
};

5.4 合并二叉树

var mergeTrees = function(root1, root2) {
    if(!root1) return root2;
    if(!root2) return root1;
    if(root1&&root2) root1.val+=root2.val
    root1.left = mergeTrees(root1.left,root2.left);
    root1.right = mergeTrees(root1.right,root2.right);
    return root1;
};

5.5 对称二叉树

构造一个辅助函数判断两棵树是否是镜像对称的,然后题目只要判断两棵这个树是否镜像对称
而比较两棵树是否镜像对称,即一棵树的左子树和另一棵树的右子树,以及一棵树的右子树和另一棵树的左子树是否镜像对称

var isSymmetric = function(root) {
    return isMirror(root, root);
};
var isMirror =function(p,q){
    if(!q&&!p) return true;
    if(!q || !p) return false;
    return (p.val==q.val) && isMirror(p.left,q.right)&&isMirror(p.right,q.left);
}

5.6 判断一棵树是否为另一颗树的子结构

var isSubStructure = function(A, B) {
    if(!A || !B)   return false;
    #  判断A树和B树是否完全相等 或 左边的树包含B树 或 右边的树包含B树
    return isSameTree(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B)
};

var isSameTree = function(A, B) {
    // B为空,说明B树被遍历到底部了。证明B树完全存在于A树中。 返回 true
    if(!B)        return true;
    // A为空,且不满足上面的判断(即B不为空),说明A树遍历完了还没符合上面的情况,证明B树不存在于这条分支。 返回 false
    if(!A)        return false;
    // 当前节点的值不相等,不 ok
    if(A.val !== B.val)         return false;
    # 递归考察左子树、右子树
    return isSameTree(A.left, B.left) && isSameTree(A.right, B.right);
};

5.7 单值二叉树

所有节点值均相等

var isUnivalTree = function(root) {
    if(!root) return true;
    if(root.left&&root.left.val!=root.val || root.right && root.right.val != root.val)
        return false;
    return isUnivalTree(root.left) && isUnivalTree(root.right)
};

5.8 重建二叉树

给定节点数为 n 的二叉树的前序遍历中序遍历结果,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
在这里插入图片描述
提示:
1.vin.length == pre.length
2.pre 和 vin 均重复元素
3.vin出现的元素均出现在 pre里
4.只需要返回根结点,系统会自动输出整颗树做答案对比
数据范围:n \le 2000n≤2000,节点的值 -10000 \le val \le 10000−10000≤val≤10000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

输入:
[1,2,4,7,3,5,6,8],[4,7,2,1,5,3,8,6]
复制
返回值:
{1,2,3,4,#,5,6,#,7,#,#,8}
复制
说明:
返回根节点,系统会输出整颗二叉树对比结果,重建结果如题面图示  
function reConstructBinaryTree(pre, vin)
{
    // write code here
    if(!pre.length||!vin.length) return null;
    //通过pre找到根节点
    const root= new TreeNode(pre.shift());
    //左子树的长度
    const index=vin.indexOf(root.val);
    root.left=reConstructBinaryTree(pre,vin.slice(0,index));
    root.right=reConstructBinaryTree(pre,vin.slice(index+1));
    return root;
}

6 层次遍历

6.1判断是不是完全二叉树

function isCompleteTree( root ) {
    let rootArr=[];
    rootArr.push(root);
    let end=false;
    while(rootArr.length){
//         取出第一个
        const node=rootArr.shift();
        if(!node) end=true;
        else{
            if(rootArr.length && end) return false;
            rootArr.push(node.left);
            rootArr.push(node.right);
        }
    }
    return true;
 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值