随想录训练营Day14 | 二叉树理论基础, 三种遍历(递归法,迭代法)
标签: LeetCode闯关记
理论基础
1.1 种类
- 满二叉树(只有度为0的结点和度为2的结点,并且度为0的结点在同一层上)
- 完全二叉树(①除了最底层节点可能没填满之外,其余的每层节点数都达到了最大值;②最下层的节点都集中在该层的最左边的若干位置)
- 二叉搜索树(元素有顺序,但是对节点的分布没要求)
- 完全搜索数(①是空树 / 左右子树高度差不超过1; ②左右两个子树都是一颗平衡二叉树)
1.2 存储方式
- 顺序存储(用数组来实现)
- 链式存储
1.3 二叉树定义
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.4 二叉树的遍历
- 深度优先搜索---前中后序 递归法; 迭代法; 前序遍历: 中左右
- 广度优先搜索---层序搜索 迭代法;
2. 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);
}
}
- 迭代法
key:前序遍历的遍历顺序是: 中, 左孩子, 右孩子; 但是,栈的特性是LIfO, 所以入栈顺序是先右孩子,再左孩子,这样弹出(即遍历)的顺序就能够使先左孩子
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) {
//前序遍历的遍历顺序是: 中, 左孩子, 右孩子; 但是,栈的特性是LIfO, 所以入栈顺序是先右孩子,再左孩子,这样弹出(即遍历)的顺序就能够使先左孩子
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return result;
}
}
3. 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);//右
}
}
- 迭代法:
关键点: 遍历的顺序和处理的顺序不一样 栈stack用来记录被遍历过的元素, result用来记录被处理过的元素, 指针cur来访问节点,访问到最底层;
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
//关键点: 遍历的顺序和处理的顺序不一样
//栈stack用来记录被遍历过的元素, result用来记录被处理过的元素, 指针cur来访问节点,访问到最底层;
if (root == null) {
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.add(cur);
cur = cur.left;
} else {
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
4. 145.二叉树的后序遍历
- 递归法
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
//确定参数和返回值 参数:root,并用一个List来存储遍历到的节点;返回值:void
//确定每层遍历的顺序: 左右中
//确定终止的条件: root == null;
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);//中
}
}
- 迭代法(根据迭代法的前序遍历,改两处即可)
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
//根据迭代法的前序遍历,改两处即可
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root == null){
return result;
}
while(! stack.isEmpty()){
stack.push(root);
TreeNode node = stack.pop();
result.add(node);
if(node.left != null){//遍历顺序变为: 先左后右
stack.push(node.left);
}
if(node.right != null){
stack.push(node.right);
}
}
Collections.reverse(result);//再翻转
return result;
}
}
5. 总结:
- 递归法: 把握住三点:
- 确定参数和返回值 参数:root,并用一个List来存储遍历到的节点;返回值:void
- 确定每层遍历的顺序;
- 确定终止的条件: root == null;
- 迭代法:
- 前序遍历的遍历顺序和处理顺序一致;
- 后序遍历只需要稍作改变就可以套用前序遍历的code;
- 中序遍历:需要单独处理, 需要引入TreeNode cur 指针指向正在遍历的元素;