第十四天| 第六章 二叉树part01 理论基础 递归遍历 迭代遍历 统一迭代
一、二叉树理论基础
1.1 结构
二叉树是这么一种树状结构:每个节点最多有两个孩子,左孩子和右孩子
重要的二叉树结构
- 完全二叉树(complete binary tree)是一种二叉树结构,除最后一层以外,每一层都必须填满,填充时要遵从先左后右
- 平衡二叉树(balance binary tree)是一种二叉树结构,其中每个节点的左右子树高度相差不超过 1
1.2 存储
存储方式分为两种
- (1)定义树节点与左、右孩子引用(TreeNode)
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
public TreeNode(TreeNode left, int val, TreeNode right) {
this.left = left;
this.val = val;
this.right = right;
}
public String toString() {
return String.valueOf(this.val);
}
}
-
(2)使用数组,前面讲堆时用过,若以 0 作为树的根,索引可以通过如下方式计算
-
父 = floor((子 - 1) / 2)
-
左孩子 = 父 * 2 + 1
-
右孩子 = 父 * 2 + 2
-
1.3 遍历
遍历也分为两种
- 广度优先遍历(Breadth-first order):尽可能先访问距离根最近的节点,也称为层序遍历
- 深度优先遍历(Depth-first order):对于二叉树,可以进一步分成三种(要深入到叶子节点)
- pre-order 前序遍历,对于每一棵子树,先访问该节点,然后是左子树,最后是右子树
- in-order 中序遍历,对于每一棵子树,先访问左子树,然后是该节点,最后是右子树
- post-order 后序遍历,对于每一棵子树,先访问左子树,然后是右子树,最后是该节点
1.3.1 广度优先遍历
如果使用数组存储二叉树,按照数组索引的顺序遍历即为广度优先遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNc7B0hA-1692716841647)(E:.STU\【JAVA】\LeetCode\第14天\二叉树广度优先遍历.png)]
1.3.2 深度优先遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P65i8LUV-1692716841648)(E:.STU\【JAVA】\LeetCode\第14天\二叉树深度优先遍历.png)]
前序遍历:中左右
中序遍历:左中右
后序遍历:左右中
下面给出二叉树的深度优先遍历的递归实现
二、递归遍历
在开始之前明确两个问题:
-
问题一:什么时候适合使用递归呢?
-
计算机科学中,递归是一种解决计算问题的方法,其中解决方案取决于同一类问题的更小子集
-
自己调用自己,如果说每个函数对应着一种解决方案,自己调用自己意味着解决方案是一样的(有规律的)
-
-
问题二:我一直没搞清楚,递归中满足终止条件return后,为什么还会向上调用,从两个角度解释一下这个问题:
-
第一,从递归的概念上看,内层函数调用(子集处理)完成,外层函数才能算调用完成
- 深入到最里层叫做递
- 从最里层出来叫做归
- 在递的过程中,外层函数内的局部变量(以及方法参数)并未消失,归的时候还可以用到
-
第二,方法的调用底层使用的数据结构是**栈**,每次调用就是一次压栈,当一个方法执行到不满足条件或者执行结束后就会弹栈,所以这就是为什么return之后仍会调用上层函数,简单来说:上层函数还在栈中。
-
-
递归三部曲:
-
在二叉树这一章处理问题时刻要把握的思想:
-
(1)确定递归函数的参数和返回值;
-
(2)确定终止条件;
-
(3)确定单层递归的逻辑。
-
-
-
LeetCode给出的二叉树定义:
// 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;
}
}
2.1 前序遍历(144. 二叉树的前序遍历)
-
题目链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/
-
代码:
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);
}
}
2.2 中序遍历(94. 二叉树的中序遍历)
-
题目链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/
-
代码:
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);
}
}
2.3 后序遍历(145. 二叉树的后序遍历)
-
题目链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/
-
代码:
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);
}
}