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

news2025/3/5 9:39:52

遍历

递归

递归三部曲
1.参数和返回值
2.终止条件
3.单层逻辑(遍历顺序

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;

迭代

借助队列
前序和后序
在这里插入图片描述

前序遍历:
// 入栈 右 -> 左
// 出栈 中 -> 左 -> 右
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();
};

中序

中序遍历:

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

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;
};

层序

队列 当前层元素个数

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;
};

应用

反转二叉树

前序,若中序注意调整

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;
  }

对称二叉树

比较逻辑后序遍历

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细节

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而是另外计算算法
在这里插入图片描述

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;
}

完全二叉树的节点个数

普通二叉树解决

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)
}

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

  
    //**终止条件
    // 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 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度

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回溯算法大全——组合、分割、子集、排列和棋盘问题
在这里插入图片描述

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

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)
平时我们解二叉树的题目时,已经习惯了通过节点的左右孩子判断本节点的属性,而本题我们要通过节点的父节点判断本节点的属性。

后序遍历

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);
}

找树左下角的值

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;
}

路径总和⭐⭐

回溯算法

/**
 * 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⭐⭐

处理根节点逻辑

/**
 * 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;
}

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

在这里插入图片描述

/**
 * 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;
};

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

/**
 * 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;
};

最大二叉树

在这里插入图片描述

/**
 * 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;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2309946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考-数据库开发工程师-3.1-数据结构-线性结构

第3章内容比较多&#xff0c;内容考试分数占比较大&#xff0c;6分左右 线性表 1、线性表的定义 一个线性表是n个元素的有限序列(n≥0)&#xff0c;通常表示为(a1&#xff0c;a2, a3,…an). 2、线性表的顺序存储(顺序表) 是指用一组地址连续的存储单元依次存储线性表中的数据元…

【五.LangChain技术与应用】【2.LangChain虚拟环境搭建(下):环境优化与调试】

一、Docker化部署:别让你的环境成为薛定谔的猫 经历过"在我机器上能跑"惨案的老铁都懂,传统虚拟环境就像个黑盒子。去年我帮客户部署LangChain应用,因为glibc版本差了0.1,整个服务直接崩成烟花。从那天起,我所有项目都强制上Docker! Dockerfile生存指南: #…

deepseek+mermaid【自动生成流程图】

成果&#xff1a; 第一步打开deepseek官网(或百度版&#xff08;更快一点&#xff09;)&#xff1a; 百度AI搜索 - 办公学习一站解决 第二步&#xff0c;生成对应的Mermaid流程图&#xff1a; 丢给deepseek代码&#xff0c;或题目要求 生成mermaid代码 第三步将代码复制到me…

在 Element Plus 的 <el-select> 组件中,如果需要将 <el-option> 的默认值设置为 null。 用于枚举传值

文章目录 引言轻松实现 `<el-option>` 的默认值为 `null`I 实现方式监听清空事件 【推荐】使用 v-model 绑定 null添加一个值为 null 的选项处理 null 值的显示引言 背景:接口签名规则要求空串参与,空对象不参与签名计算 // 空字符串“” 参与签名组串,null不参与签…

解码未来!安徽艾德未来智能科技有限公司荣获“GAS消费电子科创奖-产品创新奖”!

在2025年“GAS消费电子科创奖”评选中&#xff0c;安徽艾德未来智能科技有限公司提交的“讯飞AI会议耳机iFLYBUDS Pro 2”&#xff0c;在技术创新性、设计创新性、工艺创新性、智能化创新性及原创性五大维度均获得评委的高度认可&#xff0c;荣获“产品创新奖”。 这一殊荣不仅…

力扣hot100刷题——栈

文章目录 69.有效的括号题目描述思路&#xff1a;栈code 70.最小栈题目描述思路&#xff1a;双栈法code优化&#xff1a;单栈法code 71.字符串解码题目描述思路&#xff1a;栈code 73.每日温度题目描述思路&#xff1a;单调栈code 74.柱状图中最大的矩形题目描述思路&#xff1…

TMS320F28P550SJ9学习笔记2:Sysconfig 配置与点亮LED

今日学习使用Sysconfig 对引脚进行配置&#xff0c;并点亮开发板上的LED4 与LED5 我的单片机开发板平台是 LAUNCHXL_F28P55x 我是在上文描述的驱动库C2000ware官方例程example的工程基础之上进行添加功能的 该例程路径如下&#xff1a;D:\C2000Ware_5_04_00_00\driverlib\f28p…

健康养生:开启活力人生的钥匙

在快节奏的现代生活中&#xff0c;健康养生已成为我们追求美好生活的关键。它不仅关乎身体的强健&#xff0c;更与心灵的宁静息息相关。 合理饮食是健康养生的基石。多吃蔬菜、水果&#xff0c;它们富含维生素与矿物质&#xff0c;为身体提供充足养分。全谷物食品也是不错的选…

HTTP 与 HTTPS 协议:从基础到安全强化

引言 互联网的消息是如何传递的&#xff1f; 是在路由器上不断进行跳转 IP的目的是在寻址 HTTP 协议&#xff1a;互联网的基石 定义 HTTP&#xff08;英文&#xff1a;HyperText Transfer Protocol&#xff0c;缩写&#xff1a;HTTP&#xff09;&#xff0c;即超文本传输协…

【Linux】冯诺依曼体系结构-操作系统

一.冯诺依曼体系结构 我们所使用的计算机&#xff0c;如笔记本等都是按照冯诺依曼来设计的&#xff1a; 截止目前&#xff0c;我们所知道的计算机都是由一个一个的硬件组装起来的&#xff0c;这些硬件又由于功能的不同被分为了输入设备&#xff0c;输出设备&#xff0c;存储器…

mapbox进阶,使用点类型geojson加载symbol符号图层,用于标注带图标的注记,且文字居中在图标内,图标大小自适应文字

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;mapbox 从入门到精通 文章目录 一、&#x1f340;前言1.1 ☘️mapboxgl.Map 地图对象…

布隆过滤器(附带位图讲解)

提到位图&#xff0c;我们首先想到的应该是它的两面定义&#xff1a; 位图图像&#xff08;bitmap&#xff09;&#xff0c;亦称为点阵图或栅格图像&#xff0c;是由称作像素&#xff08;图片元素&#xff09;的单个点组成的。位图是指内存中连续的二进制位&#xff0c;用于对…

CC++的内存管理

目录 1、C/C内存划分 C语言的动态内存管理 malloc calloc realloc free C的动态内存管理 new和delete operator new函数和operator delete函数 new和delete的原理 new T[N]原理 delete[]的原理 1、C/C内存划分 1、栈&#xff1a;存有非静态局部变量、函数参数、返回…

【商城实战(2)】商城架构设计:从底层逻辑到技术实现

【商城实战】专栏重磅来袭&#xff01;这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建&#xff0c;运用 uniapp、Element Plus、SpringBoot 搭建商城框架&#xff0c;到用户、商品、订单等核心模块开发&#xff0c;再到性能优化、安全加固、多端适配&#xf…

USB 模块 全面解析(一)

本文是我整理的一些 USB 的学习心得&#xff0c;希望能对大家有所帮助。 文章目录 前言&#x1f352; USB 基本概述&#x1f352; USB 结构框架&#x1f349;硬件框架&#x1f349; 软件框架 &#x1f352; USB 电气信号&#x1f349; USB 硬件线路&#x1f349; 信号电平&…

xr-frame 3D Marker识别,扬州古牌坊 3D识别技术稳定调研

目录 识别物体规范 3D Marker 识别目标文件 map 生成 生成任务状态解析 服务耗时&#xff1a; 对传入的视频有如下要求&#xff1a; 对传入的视频建议&#xff1a; 识别物体规范 为提高Marker质量&#xff0c;保证算法识别效果&#xff0c;可参考Marker规范文档 Marker规…

拆一拆吉利普及高阶智驾的盲盒

吉利银河后续所有的全新和改款车型都会搭载千里浩瀚不同级别的智驾系统&#xff1b; 既然银河都标配了&#xff0c;定位更高的领克大概率也会全系标配&#xff1b; 加上极氪从去年下半年就是全系标配。 这样一来&#xff0c;就是吉利版的「全民智驾」。 一、 「千里浩瀚」&a…

2024四川大学计算机考研复试上机真题

2024四川大学计算机考研复试上机真题 2024四川大学计算机考研复试机试真题 历年四川大学计算机考研复试机试真题 在线评测&#xff1a;https://app2098.acapp.acwing.com.cn/ 分数求和 题目描述 有一分数序列&#xff1a; 2/1 3/2 5/3 8/5 13/8 21/13… 求出这个数列的前 …

基于提示驱动的潜在领域泛化的医学图像分类方法(Python实现代码和数据分析)

摘要 医学图像分析中的深度学习模型易受数据集伪影偏差、相机差异、成像设备差异等导致的分布偏移影响&#xff0c;导致在真实临床环境中诊断不可靠。领域泛化&#xff08;Domain Generalization, DG&#xff09;方法旨在通过多领域训练提升模型在未知领域的性能&#xff0c;但…

深度学习-大白话解释循环神经网络RNN

目录 一、RNN的思想 二、RNN的基本结构 网络架构 ​关键点 三、RNN的前向传播 四、RNN的挑战:梯度爆炸和梯度消失 问题分析 ​示例推导 五、LSTM:RNN的改进 核心组件 ​网络架构 3. LSTM 的工作流程 4. 数学公式总结 5. LSTM 的优缺点 ​优点 ​缺点 6. LSTM 的…