【数据结构】二叉树总结篇

遍历

递归

递归三部曲

1.参数和返回值

2.终止条件

3.单层逻辑(遍历顺序

javascript 复制代码
var preorderTraversal = function(root) {
// 第一种
  let res=[];
  const dfs=function(root){
      if(root===null)return ;
      //先序遍历所以从父节点开始
      res.push(root.val);
      //递归左子树
      dfs(root.left);
     //递归右子树
      dfs(root.right);
  }
  //只使用一个参数 使用闭包进行存储结果
  dfs(root);
  return res;

迭代

借助队列

前序和后序

javascript 复制代码
前序遍历:
// 入栈 右 -> 左
// 出栈 中 -> 左 -> 右
var preorderTraversal = function(root, res = []) {
    if(!root) return res;
    const stack = [root];
    let cur = null;
    while(stack.length) {
        cur = stack.pop();
        res.push(cur.val);
        cur.right && stack.push(cur.right);
        cur.left && stack.push(cur.left);
    }
    return res;
};

// 入栈 左 -> 右
// 出栈 中 -> 右 -> 左 结果翻转

var postorderTraversal = function(root, res = []) {
    if (!root) return res;
    const stack = [root];
    let cur = null;
    do {
        cur = stack.pop();
        res.push(cur.val);
        cur.left && stack.push(cur.left);
        cur.right && stack.push(cur.right);
    } while(stack.length);
    return res.reverse();
};

中序

javascript 复制代码
中序遍历:

// 入栈 左 -> 右
// 出栈 左 -> 中 -> 右

var inorderTraversal = function(root, res = []) {
    const stack = [];
    let cur = root;
    while(stack.length || cur) {
        if(cur) {
            stack.push(cur);
            // 左
            cur = cur.left;
        } else {
            // --> 弹出 中
            cur = stack.pop();
            res.push(cur.val); 
            // 右
            cur = cur.right;
        }
    };
    return res;
};

层序

队列 当前层元素个数

javascript 复制代码
var levelOrder = function(root) {
    //二叉树的层序遍历
    let res = [], queue = [];
    queue.push(root);
    if(root === null) {
        return res;
    }
    while(queue.length !== 0) {
        // 记录当前层级节点数!!!
        let length = queue.length;
        //存放每一层的节点!!!
        let curLevel = [];
        for(let i = 0;i < length; i++) {
            let node = queue.shift();
            curLevel.push(node.val);
            // 存放当前层下一层的节点
            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        }
        //把每一层的结果放到结果数组
        res.push(curLevel);
    }
    return res;
};

应用

反转二叉树

前序,若中序注意调整

javascript 复制代码
var invertTree = function(root) {
    //1.前序
    //递归
    if(root==null)return null;
    //中,交换左右节点
    swap(root)
    //左
    invertTree(root.left);
    //右
    invertTree(root.right);
    return root ;
    //中序遍历的话:左中右(右传的也是左指针,因为先翻完左树,再交换左右树,交换完的左数成右树了,所以还得传左!!!)

    //迭代法
    if(!root)return null;
    let stack=[root];
    while(stack.length>0){
        let cur=stack.pop();
        swap(cur)
        if(cur.left)stack.push(cur.left);
        if(cur.right)stack.push(cur.right);
    }
    return root;

    // //层次遍历(广度优先)->队列
    if(!root)return null;
    let queue=[root];
    while(queue.length>0){
        let levelSize=queue.length;
        for(let i=0;i<levelSize;i++){
            let cur=queue.shift();
            swap(cur);
            if(cur.left)queue.push(cur.left);
            if(cur.right)queue.push(cur.right)
        }
    }
    return root;

};

function swap(node){
        left=node.left;
        node.left=node.right;
        node.right=left;
  }

对称二叉树

比较逻辑后序遍历

javascript 复制代码
var isSymmetric = function(root) {
    //比较节点
    const compare=function(root1,root2){
         //终止条件!!!,默认遇到底就返回true,所以只考虑那些为false的条件
         if(!root1 && !root2)return true;
         else if(root1===null&&root2!==null||root2===null&&root1!==null)return false;
         else if(root1.val!==root2.val) return false
         
         //单层递归逻辑
        let outside=compare(root1.left,root2.right);
        let inside=compare(root1.right,root2.left);
        return outside&&inside;
    }
    if(root === null) {
        return true;
    }
    return compare(root.left,root.right)  
};

最大深度

后序 +1细节

javascript 复制代码
var maxDepth = function(root) {
    //递归方法
    if(root===null){
        return 0
    }else{
        let left=maxDepth(root.left);
        let right=maxDepth(root.right);
        return Math.max(left,right)+1;
    }
};

最小深度

考虑左侧或右侧为空的情况,最小深度不是1而是另外计算算法

javascript 复制代码
var minDepth = function(root) {
    //递归
    //重点:递归终止条件
    if(root===null)return 0;
    //叶子节点
    // if(!root.left&&!root.right) return 1;
    if(root.left===null){
        return minDepth(root.right)+1;
    }
    if(root.right===null){
        return minDepth(root.left)+1;
    }
     let left=minDepth(root.left);
     let right=minDepth(root.right);
    
    let result=Math.min(left,right)+1; 
    return  result;
}

完全二叉树的节点个数

普通二叉树解决

javascript 复制代码
var countNodes = function(root) {
     //递归方法
     const getNumbers=function(root){
        //终止条件
        if(!root)return 0;
        let left=getNumbers(root.left);
        let right=getNumbers(root.right);
        return left+right+1;
     }
    return getNumbers(root)
}

利用完全二叉树性质+递归方法

javascript 复制代码
  
    //**终止条件
    // if(!root)return 0;
    // //定义左右节点
    // let left=root.left;
    // let right=root.right;
    // let leftDepth=0,rightDepth=0;
    // while(left){
    //     left=left.left;
    //     leftDepth++;
    // }
    // while(right){
    //     right=right.right;
    //     rightDepth++;
    // }
    // if(leftDepth===rightDepth){
    //     return Math.pow(2,leftDepth+1)-1;
    // }
    // return countNodes(root.left)+countNodes(root.right)+1;

平衡二叉树⭐

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1

-1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度

javascript 复制代码
var isBalanced = function(root) {
    //  //还是用递归三部曲 + 后序遍历 左右中 
    //  //!!!当前左子树右子树高度相差大于1就返回-1,标识相差大于1的!!!!
    
    const getDepth=function(node){
        if(!node)return 0;//不为-1的所有数,用于标识
        //确定单层遍历逻辑
        let leftDepth=getDepth(node.left);//左子树高度
        // 当判定左子树不为平衡二叉树时,即可直接返回-1
        if(leftDepth===-1)return -1;

        let rightDepth=getDepth(node.right);
        if(rightDepth===-1)return -1;

        //判断左右子树高差
        if(Math.abs(leftDepth-rightDepth>1)){
            return -1;
        }else{
            return 1 + Math.max(leftDepth,rightDepth);
        }
    }
    return !(getDepth(root)===-1) 

};

二叉树的所有路径

回溯+递归算法
Javascript回溯算法大全------组合、分割、子集、排列和棋盘问题

回溯算法,就是将问题其抽象一棵树,而本次二叉树所有路径本身就是一棵树

javascript 复制代码
var binaryTreePaths = function(root) {
    let res=[];
    const backtracking=(root,value)=>{
        //到叶子节点就返回
        if(!root.left&&!root.right){
            value+=root.val;
            res.push(value);
            return;
        }
        //前序,简单的回溯
       value+=root.val+"->";
       root.left&&backtracking(root.left,value);
       root.right&&backtracking(root.right,value)
    }
    backtracking(root,'');
    return res;
};

左叶子之和⭐

判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子: if(node.left !== null && node.left.left === null && node.left.right === null)

平时我们解二叉树的题目时,已经习惯了通过节点的左右孩子判断本节点的属性,而本题我们要通过节点的父节点判断本节点的属性。

后序遍历

javascript 复制代码
var sumOfLeftLeaves = function(root) {
    //迭代
    let sum=0;
    let stack=[root];
    if(!root)return 0;
    while(stack.length){
        let cur=stack.pop();
        if(cur.left&&!cur.left.left&&!cur.left.right){
            sum+=cur.left.val;
        }
        cur.left&&stack.push(cur.left);
        cur.right&&stack.push(cur.right);
    }
    return sum;
}


var sumOfLeftLeaves = function(root) {
    //采用后序遍历 递归遍历
    // 1. 确定递归函数参数
    const nodesSum = function(node) {
        // 2. 确定终止条件
        if(!node) {
            return 0;
        }
        let leftValue = nodesSum(node.left);
        let rightValue = nodesSum(node.right);
        // 3. 单层递归逻辑
        let midValue = 0;
        if(node.left && !node.left.left && !node.left.right) {
            midValue = node.left.val;
        }
        let sum = midValue + leftValue + rightValue;
        return sum;
    }
    return nodesSum(root);
}

找树左下角的值

javascript 复制代码
var findBottomLeftValue = function(root) {
    //Todo:最大深度,最左侧的值
    // 层次遍历
    let queue=[root];
    if(!root)return null;
    let res;
    while(queue.length){
        let length=queue.length;
        for(let i=0;i<length;i++){
            let curNode=queue.shift();
            if(i===0){
                res=curNode.val;
            }
            curNode.left&&queue.push(curNode.left);
            curNode.right&&queue.push(curNode.right);
        }
    }
    return res;
}

路径总和⭐⭐

回溯算法

javascript 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
var hasPathSum = function(root, targetSum) {
    //targetSum依次往下减,直至减到0且找到叶子节点,就找到路径
    if(!root)return false;
    let backtracking=(root,targetSum)=>{
        //到根节点且刚好和为targetSum
        if(targetSum===0&&!root.left&&!root.right) return true;
        // 遇到叶子节点而没有找到合适的边(计数不为0),直接返回
        if (!root.left&&!root.right) return false;

        if(root.left){
            targetSum-=root.left.val
            if(backtracking(root.left,targetSum)) return true;
            targetSum+=root.left.val
        }
        if(root.right){
            targetSum-=root.right.val
            if(backtracking(root.right,targetSum)) return true;
            targetSum+=root.right.val
        }
        return false;
    }
    return backtracking(root,targetSum-root.val);
};

路径总和II⭐⭐

处理根节点逻辑

javascript 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {number[][]}
 */
var pathSum = function(root, targetSum) {
    let res=[]
    if(!root)return res;
    let path=[root.val];

    let backtracking=(root,sum)=>{
        if(!root.left&&!root.right&&sum===targetSum){
            res.push(path.slice());
            return;
        }
        if(!root.left&&!root.right)return;
        //左
        if(root.left){
            path.push(root.left.val);
            sum+=root.left.val;
            backtracking(root.left,sum);
            sum-=root.left.val;
            path.pop();
        }
        //右
        if(root.right){
            path.push(root.right.val);
            sum+=root.right.val;
            backtracking(root.right,sum);
            sum-=root.right.val;
            path.pop();
        }
        return;
    }
    backtracking(root,root.val)
    return res;
}

从中序与后序遍历序列构造二叉树

javascript 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} inorder
 * @param {number[]} postorder
 * @return {TreeNode}
 */
var buildTree = function(inorder, postorder) {
    //中序:左中右
    //后序:左右中
    //返回条件
    if(!postorder.length)return null;
    //1.从后序找到中间节点
    let midVal=postorder.pop();
    let root=new TreeNode(midVal);
    //左中序,右后序
    let index=inorder.indexOf(midVal);//找到中序的mid对应的索引
    root.left=buildTree(inorder.slice(0,index),postorder.slice(0,index));
    root.right=buildTree(inorder.slice(index+1),postorder.slice(index));
    return root;
};

从前序与中序遍历序列构造二叉树

javascript 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
var buildTree = function(preorder, inorder) {
    //前序:中左右
    //中序:左中右
    if(!preorder.length)return null;
    let midVal=preorder.shift();
    let index=inorder.indexOf(midVal);
    let root=new TreeNode(midVal);
    root.left=buildTree(preorder.slice(0,index),inorder.slice(0,index));
    root.right=buildTree(preorder.slice(index),inorder.slice(index+1));
    return root;
};

最大二叉树

javascript 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var constructMaximumBinaryTree = function(nums) {
    //构建二叉树类型题目------前序遍历
    if(!nums.length)return null;
    //1.找最大
    let max=-1,maxIndex=-1;
    for(let i=0;i<nums.length;i++){
        if(nums[i]>max){
            max=nums[i];
            maxIndex=i;
        }
    }

    //2.构建根节点
    let root =new TreeNode(max);
    //构建左边
    root.left=constructMaximumBinaryTree(nums.slice(0,maxIndex));
    //构建右边
    root.right=constructMaximumBinaryTree(nums.slice(maxIndex+1));
    return root;
};
相关推荐
神秘的t6 分钟前
优选算法合集————双指针(专题三)
java·数据结构·算法·二分查找
yinxiangzhongqing24 分钟前
深入理解JavaScript的执行机制
开发语言·前端·javascript
小画家~34 分钟前
第五十八:父传子 defineProps
前端·javascript·vue.js
青红光硫化黑37 分钟前
前端基础之组件
前端·javascript
没资格抱怨1 小时前
Element Plus中的树组件的具体用法(持续更新!)
前端·javascript·算法
86Eric1 小时前
vue Table 表格自适应窗口高度,表头固定
javascript·vue.js·table 高度自适应·table 表头固定
智绘前端2 小时前
Vue3国际化开发实战:i18n-Ally + vue-i18n@next高效配置教程,项目中文显示
前端·javascript·vue.js
Wuxiaoming1353 小时前
flutter的debounce_throttle插件使用
开发语言·javascript·flutter
三次元1113 小时前
什么是闭包?(写给前端小白的理解笔记)
前端·javascript·笔记·ecmascript
醉城夜风~3 小时前
[数据结构]设计循环队列
数据结构·算法