目录
递归遍历:144. 二叉树的前序遍历 - 力扣(LeetCode)
层序遍历:102. 二叉树的层序遍历 - 力扣(LeetCode)
囿于市井,面向山海,远隔巷弄,且看花开。
递归遍历:144. 二叉树的前序遍历 - 力扣(LeetCode)
递归算法三要素:
-
确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
-
确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
-
确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
以前序遍历为例:
1) 确定递归函数的参数和返回值:因为要打印出前序遍历节点的数值,所以参数里需要传入List来放节点的数值,除了这一点就不需要再处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:
private void preorder(TreeNode node, List<Integer> result)
2) 确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:
if (node == null){ return; }
3) 确定单层递归的逻辑:前序遍历是中左右的顺序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:
result.add(node.val); // 访问根节点
preorder(node.left, result); // 递归访问左子树
preorder(node.right, result); // 递归访问右子树
递归遍历(前序)完整代码:
class Solution144 {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
preorder(root, result);
return result;
}
// 递归实现前序遍历
private void preorder(TreeNode node, List<Integer> result){
if (node == null){
return;
}
result.add(node.val); // 访问根节点
preorder(node.left, result); // 递归访问左子树
preorder(node.right, result); // 递归访问右子树
}
}
迭代遍历:
迭代方法使用栈来模拟递归过程,有时在处理大数据量或避免栈溢出时更为适用。
// 迭代方法使用栈来模拟递归过程,有时在处理大数据量或避免栈溢出时更为适用。
private List<Integer> preorderTraversal2(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.pop(); // 弹出栈顶元素
result.add(node.val); // 按顺序访问节点
// 注意:先推右孩子,再推左孩子
if (node.right != null) {
stack.push(node.right); // 右孩子入栈
}
if (node.left != null) {
stack.push(node.left); // 左孩子入栈
}
}
return result;
}
而对于中序遍历(左中右)和后续遍历(左右中)的代码,只需要交换访问根节点与左右子树的顺序即可。
---------------------------------------------------------------------------------------------------------------------------
区别:
- 递归方法:通过系统调用栈来实现前序遍历,直观且易于实现,但在深度较大的树上可能会导致栈溢出。
- 迭代方法:使用显式的栈来控制遍历过程,更适合处理大规模数据或深度很大的树,避免了递归可能导致的栈溢出问题。
在实际应用中,可以根据实际情况选择适合的遍历方式。如果树的深度不会特别大,通常递归方法更为简单直接。如果树的深度非常大,或者在面试中希望展示对栈的控制能力,可以选择迭代方法。
统一迭代:
🕳++;
层序遍历:102. 二叉树的层序遍历 - 力扣(LeetCode)
按照从顶层到底层,每一层从左到右的顺序遍历树的节点的方法。这种遍历方式通常使用队列(Queue)来实现。
- 初始化队列:首先将根节点加入队列。
- 处理队列中的节点:
- 在每一轮循环中,先计算当前队列的长度,这个长度就是当前层的节点数。
- 然后,通过一个循环处理当前层的所有节点,将它们的值加入到当前层结果列表中,并将它们的非空左右子节点加入到队列中。
- 添加到结果列表:每处理完一层,就将这一层的结果添加到最终的结果列表中。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root); // 根节点入队
while (!queue.isEmpty()) {
int levelSize = queue.size(); // 当前层的节点数量
List<Integer> currentLevel = new ArrayList<>(); // 存储当前层的节点值
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll(); // 节点出队
currentLevel.add(currentNode.val); // 添加节点值到当前层
// 将当前节点的左右子节点入队
if (currentNode.left != null) {
queue.offer(currentNode.left);
}
if (currentNode.right != null) {
queue.offer(currentNode.right);
}
}
result.add(currentLevel); // 将当前层的结果添加到最终结果列表
}
return result;
}
}
学会二叉树的层序遍历,可以一口气打完以下十题:
107. 二叉树的层序遍历 II - 力扣(LeetCode)
515. 在每个树行中找最大值 - 力扣(LeetCode)
116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)