算法训练营 day 14| 二叉树part 01|理论基础,遍历,迭代

OpenCV 算法梳理分析 本文系统梳理了OpenCV的核心算法体系,从模块架构到具体函数实现。OpenCV采用分层设计,包含基础I/O、图像处理、特征检测、视频分析和智能算法五大层次,所有模块基于统一的Mat数据结构。重点分析了imgproc模块的8类图像处理算法(滤波、形态学、阈值化等),features2d模块的特征检测与匹配流程(SIFT/ORB等算法对比),以及objdetect、video、calib3d等模块的核心功能。针对不同场景提供了算法选型建议,如高斯噪声推荐GaussianBlur,特征匹配推荐ORB+Flann 阅读详情

二叉树理论基础

文章链接:代码随想录

二叉树种类:

满二叉树 full binary tree,

完全二叉树 complete binary tree,除了最后一排,全满,最后一排连续

二叉搜索树 binary search tree,

平衡二叉搜索树 AVL tree, 二叉搜索树+左右子树高度差为1

二叉树储存方式:

链式储存/数组储存 (顺序存储:左节点位置:2i+1;右节点位置:2i+2; i为父节点位置)

遍历方式:

DFS:

前序;中序;后序;遍历左右的时候遍历的是左右子树,再在子树中执行前/中/后序遍历

递归法迭代法

BFS:

层序遍历

迭代法

二叉树定义

class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;
    public TreeNode(int val){
        this.val = val;
    }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

}

二叉树的递归遍历

递归算法的三个要素:

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null) {
            return;
        }
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
    }
}
// 中序遍历·递归·LC94_二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inorder(root.left, list);
        list.add(root.val);             // 注意这一句
        inorder(root.right, list);
    }
}
// 后序遍历·递归·LC145_二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);             // 注意这一句
    }
}

list.add(root.val)在哪里就代表中在哪里

前序遍历 preorder: 中左右

后序遍历 postorder: 左右中

中序遍历 inorder:左中右

二叉树的迭代遍历

前序和后序遍历,访问的元素和要处理的元素是一致的都是中间节点(后序遍历可以由变体的前序遍历,反转得到)

但是中序遍历,处理顺序和访问顺序是不一致的。(先访问到最底部,再开始处理节点)

中序遍历为栈遍历和指针遍历的结合

// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right != null){
                stack.push(node.right);
            }
            if (node.left != null){
                stack.push(node.left);
            }
        }
        return result;
    }
}

// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        //中序与其他不同的是,进入循环才在栈里加入第一个节点
        while (cur != null || !stack.isEmpty()){
           if (cur != null){
               stack.push(cur);
               cur = cur.left;
           }else{
               cur = stack.pop();
               result.add(cur.val);
               cur = cur.right;
           }
        }
        return result;
    }
}

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.left != null){
                stack.push(node.left);
            }
            if (node.right != null){
                stack.push(node.right);
            }
        }
        Collections.reverse(result);
        return result;
    }
}

二叉树的统一迭代法

将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记:要处理的节点放入栈之后,紧接着放入空指针做标记(已经遍历过一次)


class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }
}

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
    Stack<TreeNode> st = new Stack<>();
    if (root != null) st.push(root);
    while (!st.empty()) {
        TreeNode node = st.peek();
        if (node != null) {
            st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
            if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
            st.push(node);                          // 添加中节点
            st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。

            if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
        } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
            st.pop();           // 将空节点弹出
            node = st.peek();    // 重新取出栈中元素
            st.pop();
            result.add(node.val); // 加入到结果集
        }
    }
    return result;
}
}

class Solution {
   public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)         
                               
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
   }
}

都是先加入跟节点,但是

代码的顺序跟实际上的顺序反过来,e.g

后序遍历顺序:左右中->代码顺序:中右左

CentOS 7.9安装python3.11 阅读详情

相关推荐

App 抓包难?Frida Hook 实战:绕过 SSL Pinning (证书绑定) 抓取某大厂 App 核心接口

当我们使用 Charles 抓包 HTTPS 时,本质上是实施了MITM(中间人攻击)。Charles 伪装成服务器,发给 App 一个自己的证书(伪造证书)。Charles 伪装成 App,拿着真正的证书去请求服务器。如果 App 只是简单校验“证书是否由受信任的 CA 签发”,那你把 Charles 的根证书安装到手机系统里,就能骗过它。但是,是 App 内置了服务器证书的Hash(指纹)。

BUG猿的博客 813

代码随想录算法训练营43期 | Day 13 —— 二叉树part01

int val;

当代优秀青年的学习博客 1164

IEC61000-4-5-中文版(EMC雷击浪涌)

IEC61000-4-5-中文版(EMC雷击浪涌)

代码随想录算法训练营Day 14|二叉树Part01|二叉树理论基础、递归遍历迭代遍历、统一迭代

前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树。下面这两棵树都是搜索树 又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 常用 通过指针把分布在各个地址的节点串联一起。 如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。 递归遍历 二叉树的前序遍历 二叉树的中序遍历 二叉树的后序遍历每次写递归,

m0_51759998的博客 1788

代码随想录算法训练营day14||二叉树part01理论基础、递归遍历迭代遍历、统一迭代

这里帮助大家确定下来递归算法的三个要素。

qq_40222433的博客 945

2023/7/25--代码随想录算法训练营day14|第六章 二叉树part01 理论内容、递归遍历迭代遍历、统一遍历

需要了解 二叉树的种类,存储方式,遍历方式 以及二叉树的定义:先往深走,遇到叶子结点,再往回走====使用栈:一层一层去遍历。====使用队列。

qq_46277517的博客 312

代码随想录训练营day14|LeetCode二叉树part1

代码随想录@程序员Carl快期末了,加速赶੭ ˙ᗜ˙ )੭。

xjx2022_1_12的博客 612

算法训练营Day14|二叉树part01

理论基础递归遍历迭代遍历

weixin_45898193的博客 168

算法训练营打卡Day14 | 二叉树part01

我来举一个典型的例子如题:​相信不少同学最后一个二叉树是不是完全二叉树都中招了。

weixin_51540348的博客 177

代码随想录训练营day14|二叉树part01遍历

写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。迭代法利用栈来实现,其实递归的底层实现也是栈,所以我们可以模拟栈来实现非递归遍历树。是因为对于中序遍历而言,访问和处理的节点并不同步,所以需要区分开进行处理,而对于前序和后续,可以做到访问和处理的节点同步。,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

weixin_44570996的博客 174

代码随想录算法训练营Day14| 二叉树part01

二叉树的递归遍历迭代遍历(包括前中后序遍历

weixin_57654532的博客 299

代码随想录算法训练营Day14 | 二叉树part01

【代码】代码随想录算法训练营Day14 | 二叉树part01

EE_Bee的博客 461

【代码随想录算法训练营14期】-Day14- 第六章 二叉树part01

递归法递归法遍历使用数组记录遍历顺序,前序push,left,right;中序left,push,right,后序left,right,push;迭代迭代法使用栈的特点,前序遍历一个节点就push,判断左右是否为空,不为空放入栈(先进后出,所以先右后左儿子);后序遍历为中右左,反转数组为中左右;中序遍历因为push和遍历不是同步的,故用额外指针cur遍历遍历的节点都存入栈,一直遍历左儿子,节点为空时,取出栈头(根)节点push,遍历右儿子…直到栈为空而且cur也空才停止。

jtzdgzlm的博客 267

代码训练营|day 14 任务以及具体安排:第六章 二叉树part01

后序2:再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了。后序1:先访问到最左节点,该节点的子树中,左孩子是空。中序的过程是先遍历到最左端,那么在最左端这个小树里,左节点是空,不用记录数据,所以先记录栈顶的数据,然后转到右节点。栈顶存的是当前子树的根节点。别人写的更明白一点,就是将根节点入栈,然后将其出栈,记录数据,然后将他的左右节点入栈,然后注意下入右这样,先弹的是左。

w123w_05的博客 721

leetcode 56合并区间

这道题你可以把它压缩成一个非常好记的思路:先排序,再逐个比较;能合并就扩右边界,不能合并就加入答案。具体来说,先用: 把所有区间按照左端点从小到大排序。排序之后,我们只需要拿“当前区间”和 里最后一个已经合并好的区间比较。如果: 说明前一个区间的右端点比当前区间左端点还小,二者没有重叠: 所以直接: 如果不满足: 那就说明: 两个区间重叠,例如: 此时左端点不用动,只需要把右端点扩大: 所以整道题可以记成: 这道题容易忘的函数/语法 含义:对整个 排序。对于:

qq_63176310的博客 227

小红书 算法一面 十一

例如 **Soft MoE** 采用这种方式,大幅提升负载均衡性,但会增加计算开销。- **熵正则化**:直接最大化门控概率分布的熵,熵越大表示分布越均匀,公式为 $\mathcal{L}_{ent} = -\frac{1}{N}\sum_{i=1}^N\sum_{k=1}^K p_{ik}\log p_{ik}$。- **核心原理**:源自 **GShard** 和 **Switch Transformer**,通过最小化专家选择概率分布的熵,迫使门控网络给每个专家分配相近的 token 数量。

XLYcmy的博客 430

LeetCode 1. 两数之和 | 从暴力枚举到哈希表最优解 + 空间换时间思想全复盘

文章摘要 题目要求在数组中找到两数之和等于目标值的下标。核心思路是优化查找效率: 暴力法:双重循环枚举所有数对,时间复杂度O(n²),效率低但易实现。 哈希表优化:利用哈希表O(1)查找特性,遍历时存储已访问元素,每次计算差值并查询,时间复杂度降至O(n)。关键细节是先查后存,避免自匹配。 代码实现:哈希表法通过一次遍历完成,空间换时间,边界处理需注意空数组和不足两元素的情况。 常见错误:内层循环起始点、哈希表键值设计、立即返回结果等易错点需警惕。 面试要点:优先选择哈希表解法,解释与暴力法的差异,强调时间

yc2284238579的博客 250

算法日常・每日刷题--<BFS>4

题目摘要: 给定一个由'X'和'O'组成的矩阵,要求捕获所有被'X'完全包围的'O'区域(即不与边界连通的区域),并将其替换为'X'。解法核心是逆向处理: 标记边界连通区域:遍历矩阵四边,对边界上的'O'进行BFS/DFS,标记所有与之连通的'O'为临时符号(如'.'),表示这些区域未被包围。 批量修改:遍历整个矩阵,将未被标记的'O'(被包围的)改为'X',同时恢复标记的'.'为'O'。 示例: 输入: [['X','X','X','X'], ['X','O','O','X'], ['X','X','O'

2503_92261635的博客 253

10.LeetCode算法习题讲解--滑动窗口--最大连续为一的个数

利用滑动窗口解决最大连续1的个数

Jammy_1的博客 212

问答大模型技术方案算法实现-RAPTOR树构建算法与BEG集成使用

算法核心特点 递归聚类:逐层对文档块进行聚类,构建层次结构摘要生成:为每个聚类生成摘要,捕获语义信息展开树检索:将所有节点展开,直接进行向量检索层次化检索:从根节点开始,逐层深入检索灵活聚类:支持层次聚类和基于阈值的聚类自适应:根据节点数量动态调整树结构可扩展:支持自定义摘要生成器和检索策略

qq_42773521的博客 190

AI辅助芯片制造:算法与工程化落地

《国产DRAM龙头长鑫科技上市创纪录,AI技术重塑芯片制造新范式》 国产DRAM龙头企业长鑫科技7月27日登陆科创板,创下3.31万亿元市值,成为A股"一哥"。作为大陆唯一实现DRAM大规模量产的IDM企业,长鑫科技标志着国产存储产业的重要突破。随着AI算力需求激增,DRAM市场迎来高速发展期。AI技术正在深度改变芯片制造模式,在工艺仿真、缺陷检测、设备维护等环节发挥关键作用,显著提升良率和效率。长鑫科技高管推荐的新书《AI辅助芯片制造:算法与工程化落地》系统解析了AI赋能半导体制造的实战方案

weixin_43178406的博客 5202
上一篇: 代码训练营day13|栈与队列part03
下一篇: 算法训练 day 10|栈与队列 part 01
AdrianLeon
博客等级 码龄8年 400粉丝 · 52原创
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值