代码随想录刷题|二叉树的理论基础、 二叉树的遍历 LeetCode 144、145、94、120(下)

简介: 代码随想录刷题|二叉树的理论基础、 二叉树的遍历 LeetCode 144、145、94、120

二叉树的定义

  • 跟链表的定义方式比较像
class BinaryNode<AnyType> {
    AnyType element;       // The data in the node
    BinaryNode<AnyType> left;     // Left child
    BinaryNode<AnyType> right;    // right child
    public BinaryNOode(AnyType element) {
        this(element,null,null);
    }
    public BinaryNOode(AnyType element, BinaryNode<AnyType> left,BinaryNode<AnyType> right) {
        this.element = element;
        this.right = right;
        this.left = left;
    }
}
// Definition for a binary tree node.
public class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode() {}
     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、确定单层递归的逻辑

144.二叉树的前序遍历(递归)

// 前序遍历·递归
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        // 创建返回值
        List<Integer> result = new ArrayList<>();
        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);
    }
}

94.二叉树的中序遍历(递归)

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        inOrder(root,result);
        return result;
    }
    // 确定递归的参数和返回值
    public void inOrder (TreeNode root, List<Integer> result) {
        // 确定终止条件
        if (root == null) {
            return;
        }
        // 确定单层递归的逻辑
        inOrder(root.left,result);
        result.add(root.val);
        inOrder(root.right,result);
    }
}

145.二叉树的后序遍历(递归)

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        postOrder(root,result);
        return result;
    }
    public void postOrder(TreeNode root, List<Integer> result){
        // 确定终止条件
        if (root == null) {
            return;
        }
        postOrder(root.left , result);
        postOrder(root.right , result);
        result.add(root.val);
    }
}

前、中、后序迭代遍历(迭代法)

迭代法的操作主要分为两步:

       1、访问节点

       2、处理节点

144.二叉树的前序遍历(迭代)

前序遍历时中左右,所以我们希望节点从栈中弹出来的时候也是中左右。

6b5c5287b30043f5a4615d347f0a841a.png


上图是对一个二叉树的代码模拟,画一遍更有感觉,其实循环中每次都是父节点被弹出添加,然后临走前将右节点和左节点添加进去,后面就按照左右的顺序出战

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        // 创建接收集合
        List<Integer> result = new ArrayList<>();
        // 防止空树
        if (root == null) {
            return result;
        }
        // 创建栈
        Deque<TreeNode> stack = new LinkedList<>();
        // 首先先将根节点压栈
        stack.push(root);
        // 迭代操作
        while (!stack.isEmpty()) {
            // 获取栈顶元素
            TreeNode node = stack.peek();
            // 将栈顶元素弹出,并添加到集合中
            result.add(stack.pop().val);
            // 获取节点的右子树,因为后弹出的要先压入
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return result;
    }
}

145.二叉树的后序遍历(迭代)

前序遍历的代码完成之后,如果将循环中先访问右节点再访问左节点,变换成先访问左节点再访问右节点,这样对二叉树的遍历顺序就成了 中->右->左 了,这样刚好是后序遍历的倒序,那么将最后获取到的数组反转就是后序遍历了

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        // 创建接收集合
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        // 创建栈
        Deque<TreeNode> stack = new LinkedList<>();
        // 首先先将根节点压栈
        stack.push(root);
        // 迭代操作
        while (!stack.isEmpty()) {
            // 获取栈顶元素,一会要拿着这个元素判断它的左右子树
            TreeNode node = stack.peek();
            // 将栈顶元素弹出,并添加到集合中
            result.add(stack.pop().val);
            // 压入节点的左子树,因为后弹出的要先压入
            if (node.left != null) {
                stack.push(node.left);
            }
            // 再压入节点的右子树
            if (node.right != null) {
                stack.push(node.right);
            }           
        }
        // 至此 集合中保存的其实是 中->右->左的遍历顺序
        // 将遍历到的结果反转过来就是 左->右->中的遍历顺序了
        Collections.reverse(result);
        return result;
    }
}


94.二叉树的中序遍历(迭代)

在迭代代码的时候会分两步进行操作:


1、对节点的访问:遍历节点

       2、对节点的处理:将节点中的val添加到数组中


前序遍历的顺序是中左右,我们是从根节点(中间节点)入手的,访问到了根节点之后就对根节点进行了处理


但是中序遍历的顺序是左中右,先访问的是根节点(中间节点),访问到了中间节点之后不能对中间节点进行处理,而是要继续向左子树访问,一层一层下去,知道最左边的元素,再开始对节点进行处理


所以前面只能访问,不能处理


那就只能借助指针进行访问,访问到目的元素之后再进行处理


访问:从根节点开始一路向左进行访问,只要左子树有节点,那就进行压栈,其实压的这些都是“中”


处理:当访问的节点的左侧再没有节点的时候,将栈中的栈顶弹出,再去访问弹出节点的右子树

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        // 创建接收数组
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        // 创建栈
        Deque<TreeNode> stack = new ArrayDeque<>();
        // 创建指针
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) { // 如果节点不是空,那就将节点添加到栈中,继续寻找左孩子
                stack.push(cur);
                cur = cur.left;
            } else {           // 如果节点为空了,那就说明没有左孩子了,压进去的节点可以弹出了,然后去看弹出节点的右孩子
                TreeNode node = stack.pop();
                result.add(node.val);
                cur = node.right;
            }
        }
        return result;
    }
}

前、中、后序统一迭代

统一迭代法的书写风格,待定学习

广度优先遍历

二叉树的层序遍历

120.二叉树的层序遍历 力扣


  层序遍历真的是思路简单,代码舒适呀。顾名思义,层序遍历就是对二叉树的每一层从左到右进行遍历


       层序遍历需要借助队列来实现遍历,队列是先进先出,适合层序遍历的逻辑


       有几个需要注意的点:

               1、先将根节点入队,这样在进入循环,根据根节点开始进入循环

               2、每次循环第一步,就是要判断这一层有几个节点,也就是队列的长度,获取当下的固定值,是一会要出队的个数

               3、每次出一个节点,就把它的左孩子节点和右孩子节点添加进去


class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        // 创建队列
        Deque<TreeNode> queue = new ArrayDeque<>();
        // 先将根节点添加进队列
        queue.offer(root);
        while (!queue.isEmpty()) {
            // 获取次数队列的长度,这代表着这一层有几个节点,是一会出队的界限
            int size = queue.size();
            List<Integer> temp = new ArrayList<>();
            while (size-- > 0) {
                TreeNode node = queue.poll();
                temp.add(node.val);
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            result.add(temp);
        }
        return  result;
    }
}
相关文章
|
8月前
|
Go 开发者 索引
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣33 / 81/ 153/154)(Go语言版)
本文深入探讨了LeetCode中四道关于「搜索旋转排序数组」的经典题目,涵盖了无重复和有重复元素的情况。通过二分查找的变形应用,文章详细解析了每道题的解题思路和Go语言实现代码。关键点包括判断有序区间、处理重复元素以及如何缩小搜索范围。文章还总结了各题的异同,并推荐了类似题目,帮助读者全面掌握二分查找在旋转数组中的应用。无论是初学者还是有经验的开发者,都能从中获得实用的解题技巧和代码实现方法。
346 14
|
9月前
|
算法 Go
【LeetCode 热题100】深入理解二叉树结构变化与路径特性(力扣104 / 226 / 114 / 543)(Go语言版)
本博客深入探讨二叉树的深度计算、结构变换与路径分析,涵盖四道经典题目:104(最大深度)、226(翻转二叉树)、114(展开为链表)和543(二叉树直径)。通过递归与遍历策略(前序、后序等),解析每题的核心思路与实现方法。结合代码示例(Go语言),帮助读者掌握二叉树相关算法的精髓。下一讲将聚焦二叉树构造问题,欢迎持续关注!
244 10
|
9月前
|
存储 算法 数据可视化
【二叉树遍历入门:从中序遍历到层序与右视图】【LeetCode 热题100】94:二叉树的中序遍历、102:二叉树的层序遍历、199:二叉树的右视图(详细解析)(Go语言版)
本文详细解析了二叉树的三种经典遍历方式:中序遍历(94题)、层序遍历(102题)和右视图(199题)。通过递归与迭代实现中序遍历,深入理解深度优先搜索(DFS);借助队列完成层序遍历和右视图,掌握广度优先搜索(BFS)。文章对比DFS与BFS的思维方式,总结不同遍历的应用场景,为后续构造树结构奠定基础。
479 10
|
9月前
|
Go
【LeetCode 热题100】路径与祖先:二叉树中的深度追踪技巧(力扣437 / 236 )(Go语言版)
本文深入探讨二叉树中路径与祖先问题,涵盖两道经典题目:LeetCode 437(路径总和 III)和236(最近公共祖先)。对于路径总和 III,文章分析了双递归暴力解法与前缀和优化方法,后者通过哈希表记录路径和,将时间复杂度从O(n²)降至O(n)。在最近公共祖先问题中,采用后序遍历递归查找,利用“自底向上”的思路确定最近公共祖先节点。文中详细解析代码实现与核心要点,帮助读者掌握深度追踪技巧,理解树结构中路径与节点关系的本质。这类问题在面试中高频出现,掌握其解法意义重大。
227 4
|
9月前
|
Go 索引 Perl
【LeetCode 热题100】【二叉树构造题精讲:前序 + 中序建树 & 有序数组构造 BST】(详细解析)(Go语言版)
本文详细解析了二叉树构造的两类经典问题:通过前序与中序遍历重建二叉树(LeetCode 105),以及将有序数组转化为平衡二叉搜索树(BST,LeetCode 108)。文章从核心思路、递归解法到实现细节逐一拆解,强调通过索引控制子树范围以优化性能,并对比两题的不同构造逻辑。最后总结通用构造套路,提供进阶思考方向,帮助彻底掌握二叉树构造类题目。
559 9
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
302 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
184 6
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
405 2
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
315 3
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
382 1