算法训练Day15|理论基础● 递归遍历 ● 迭代遍历● 统一迭代

简介: 算法训练Day15|理论基础● 递归遍历 ● 迭代遍历● 统一迭代

递归之谜

(17条消息) 关于递归中return的理解(最浅显易懂)_都return了为什么还在递归_Pledgee的博客-CSDN博客


(17条消息) 二叉树三种遍历(动态图+代码深入理解)_二叉树的三种遍历例题带图_杨 戬的博客-CSDN博客

二叉树

代码随想录 (programmercarl.com)


#定义:由父节点和子节点组成,且父节点只能直接分叉两个节点;结构单独抽离出来像是一颗倒立着的树。

#分类:

1.满二叉树:每个节点有两个直接子节点,且叶子节点是满的。

2.完全二叉树:父节点左子树和右子树之间的高度差小于等于1,且其子节点均居左分布,满二叉树是一种特殊的完全二叉树(左右子树高度差为0,且最底层节点满的状态符合完全二叉树的定义)。


3.二叉搜索树:满足二叉树定义的基础之上,且每个节点存储数值,且该数值有序(父节点的数值大于左子节点及其字节点的数值,小于右子节点及其所有节点上的数值,且其子节点也具有这种规律)。

4.平衡二叉搜索树(AVL 树):在二叉搜索树的基础之上,其左右子树高度差不大于1。

TreeMap:TreeMap 是一个基于红黑树实现的有序映射(键值对)容器。它根据键的自然顺序进行排序,或者根据自定义的 Comparator 进行排序。TreeMap 的插入、删除和查找操作的时间复杂度均为 O(log n)。

TreeSet:TreeSet 是一个基于红黑树实现的有序集合容器。它根据元素的自然顺序进行排序,或者根据自定义的 Comparator 进行排序。TreeSet 的插入、删除和查找操作的时间复杂度均为 O(log n)。

这两个容器都是有序的,且支持高效的插入、删除和查找操作。它们的底层实现都是基于平衡二叉搜索树,因此能够保持元素的有序性,并且具有较快的操作性能。


其他集合补充:

HashMap:HashMap 使用了数组和链表(或红黑树)的组合实现。具体来说,它使用了数组作为底层的数据结构,每个数组元素是一个链表(或红黑树)的头节点。当发生哈希冲突时,即多个键值对被映射到同一个数组索引上时,它们会以链表(或红黑树)的形式存储在同一个数组索引位置上。

HashSet:HashSet 是基于 HashMap 实现的,它使用 HashMap 的键来存储元素,而值则为一个固定的常量对象。HashSet 实际上是一个不允许重复元素的集合,它通过 HashMap 来实现元素的存储和查找。

ArrayList:ArrayList 使用了动态数组作为底层的数据结构。它可以根据需要自动扩容,并且支持随机访问和快速的插入、删除操作。ArrayList 的实现是基于数组的,当需要插入或删除元素时,可能需要进行数组的拷贝和移动操作。

LinkedList:LinkedList 使用了双向链表作为底层的数据结构。双向链表可以支持快速的插入和删除操作,但随机访问的性能较差。LinkedList 的每个节点都包含了前驱节点和后继节点的引用。

#存储方式:链式存储和顺序存储

链式存储:每个节点存储左右指针和data,每个节点通过指针串联在一起。

顺序存储:底层数据存储在内存是有序连续分布的,也即使用数组。

#遍历方式:广度优先遍历、深度优先遍历

广度优先遍历:

层序遍历(迭代法)

深度优先遍历:

前序遍历(递归法、迭代法)

中序遍历(递归法、迭代法)

后续遍历(递归法、迭代法)

前中后序的命名是以中间节点的位置判断的。前序遍历:中左右;中序遍历:左中右;后续遍历:左右中

#二叉树定义:

 1// 二叉树定义:
 2public class TreeNode {
 3    int val; // 节点的值
 4    TreeNode left; // 左子节点
 5    TreeNode right; // 右子节点
 6
 7    // 默认构造方法
 8    TreeNode() {}
 9
10    // 带有一个参数的构造方法
11    TreeNode(int val) {
12        this.val = val;
13    }
14
15    // 带有三个参数的构造方法
16    TreeNode(int val, TreeNode left, TreeNode right) {
17        this.val = val;
18        this.left = left;
19        this.right = right;
20    }
21}

递归三部曲:

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


1preorder(root, result);

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

1if (root == null) {
2            return;
3        }

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

1// 前序为例
2result.add(root.val); // 中间节点
3preorder(root.left, result); // 左节点
4preorder(root.right, result); // 右节点

LeetCode:144前序遍历

144.二叉树的前序遍历-力扣(leetcode)

方法一:递归法

1.思路

递归三部曲

确定方法形参及返回值;确定单层递归逻辑;确定终止条件。

1.思路

递归三部曲:(终止条件应该在递归逻辑中体现)

1.确定递归函数的参数和返回值;

1preorder(root, result);

2.确定终止条件

1if (root == null) {
2            return;
3        }

3.确定单层递归逻辑;

1// 前序为例
2result.add(root.val); // 中间节点
3preorder(root.left, result); // 左节点
4preorder(root.right, result); // 右节点

2.代码实现

方法一:

 1// 前序遍历
 2class Solution {
 3    public List<Integer> preorderTraversal(TreeNode root) {
 4        List<Integer> result = new ArrayList<>();
 5
 6        // 确定递归函数的参数和返回值
 7        preorder(root, result);
 8        return result;
 9    }
10
11    // 确定单层递归的逻辑
12    public void preorder(TreeNode root, List<Integer> result) {
13        // 确定终止条件
14        if (root == null) {
15            return;
16        }
17        result.add(root.val);
18        preorder(root.left, result);
19        preorder(root.right, result);
20    }
21}

3.复杂度分析

时间复杂度:O(logn).

空间复杂度:O(n).


方法二:迭代法

1.思路

为什么用栈?

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。


2.代码实现

 1// 中序遍历
 2class Solution {
 3    public List<Integer> inorderTraversal(TreeNode root) {
 4        List<Integer> result = new ArrayList<>();
 5        // 确定递归函数的参数和返回值
 6        inorder(root, result);
 7        return result;
 8        // 确定终止条件
 9    }
10    // 确定单层递归的逻辑
11    public void inorder(TreeNode root, List<Integer> result) {
12        if (root == null) {
13            return;
14        }
15        inorder(root.left, result);
16        result.add(root.val);
17        inorder(root.right, result);
18    }
19}

3.复杂度分析:

时间复杂度:O(logn)

空间复杂度:O(n),创建了result动态数组


方法二:迭代法


1.思路:

定义节点指针cur指向root,用于控制遍历方向,定义stack栈暂存节点,用于辅助遍历,当指针和栈均为空时,则退出循环,否则继续循环。很绕==


2.代码实现:

 1// 中序遍历(出栈顺序):左——中——右 入栈顺序:中——左——右
 2
 3class Solution {
 4    public List<Integer> inorderTraversal(TreeNode root) {
 5        List<Integer> result = new ArrayList<>(); // 用于存储遍历结果的列表
 6        if (root == null) {
 7            return result; // 如果根节点为空,直接返回空列表
 8        }
 9
10        Stack<TreeNode> stack = new Stack<>(); // 用于辅助遍历二叉树的栈
11        TreeNode cur = root; // 引入指针指向根节点
12        while (cur != null || !stack.isEmpty()) { // 当当前节点不为空 或 栈不为空时,进行循环
13            if (cur != null) {  // 当前节点不为空
14                stack.push(cur); // 该节点压入栈中
15                cur = cur.left; // 将挡墙节点更新为左子节点
16            } else { // 当前节点为空时
17                cur = stack.pop(); // 弹出栈顶节点
18                result.add(cur.val); // 将节点的值加入到结果集中
19                cur = cur.right; // 将当前节点更新为右子节点
20            }
21        }
22        return result; // 将当前节点更新为右子节点
23    }
24}

3.复杂度分析:

时间复杂度:O(logn)

空间复杂度:O(n),创建了result数组

LeetCode:145.后序遍历

145. 二叉树的后序遍历 - 力扣(LeetCode)

方法一:递归法

1.思路:

递归三部曲,如上。

2.代码实现:


 1// 后续遍历
 2class Solution {
 3    public List<Integer> postorderTraversal(TreeNode root) {
 4        List<Integer> result = new ArrayList<>();
 5        // 确定递归函数的参数与返回值
 6        postorder(root, result);
 7        return result;
 8
 9    }
10    // 确定单层递归的逻辑
11    public void postorder(TreeNode root, List<Integer> list) {
12        if (root == null) {
13            return;
14        }
15        postorder(root.left, list);
16        postorder(root.right, list);
17        list.add(root.val);
18    }
19}

方法二:迭代法

1.思路:

后续遍历(出栈顺序):左右中

前序遍历(出栈顺序):中左右

将前序遍历调整为:中右左

再将中右左进行反转,得到左右中,也即实现了后续迭代遍历法

2.代码实现:

1.
 1class Solution {
 2    public List<Integer> postorderTraversal(TreeNode root) {
 3        List<Integer> result = new ArrayList<>(); // 用于存储遍历结果的列表
 4        if (root == null){
 5            return result; // 如果根节点为空,直接返回空列表
 6        }
 7        Stack<TreeNode> stack = new Stack<>(); // 用于辅助遍历的栈
 8        stack.push(root); // 将根节点压入栈中
 9        while (!stack.isEmpty()){ // 当栈不为空时,进行循环
10            TreeNode node = stack.pop(); // 弹出栈顶节点
11            result.add(node.val); // 将节点的值加入到结果列表中
12            if (node.left != null){ // 如果节点的左子节点不为空,将左子节点压入栈中
13                stack.push(node.left);
14            }
15            if (node.right != null){ // 如果节点的右子节点不为空,将右子节点压入栈中
16                stack.push(node.right);
17            }
18        }
19        Collections.reverse(result); // 翻转结果列表
20        return result; // 返回遍历结果列表
21    }
22}

3.复杂度分析:

时间复杂度:O(logn)

空间复杂度:2O(n)

递归有些绕,迭代法有些抽象,还得回来一次探宝!



相关文章
|
11天前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
14天前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
36 5
|
14天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
30 2
|
14天前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
22 2
|
17天前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
24 0
|
1月前
|
存储 机器学习/深度学习 算法
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
蓝桥杯Python编程练习题的集合,涵盖了从基础到提高的多个算法题目及其解答。
60 3
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
|
21天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
1月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
25 0
|
29天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
6天前
|
算法 数据安全/隐私保护 索引
OFDM系统PAPR算法的MATLAB仿真,对比SLM,PTS以及CAF,对比不同傅里叶变换长度
本项目展示了在MATLAB 2022a环境下,通过选择映射(SLM)与相位截断星座图(PTS)技术有效降低OFDM系统中PAPR的算法实现。包括无水印的算法运行效果预览、核心程序及详尽的中文注释,附带操作步骤视频,适合研究与教学使用。