代码随想录训练营 Day14
今日任务
二叉树的理论基础
二叉树的递归遍历
二叉树的迭代遍历
二叉树的统一迭代遍历
语言:Java
二叉树的理论基础
分类
- 满二叉树:只有度为0和度为2的节点,且度为0的节点在同一层上
- 完全二叉树:只有最后一层节点没填满
- 二叉搜索树:左节点 < 根节点 < 右节点
- 平衡二叉搜索树:AVL树,在二叉搜索树的基础上,保证是平衡的(左右子树高度差不超过1)
存储方式
- 链式存储
- 顺序存储
遍历方式
- 深度优先遍历:
前序遍历:递归法,迭代法
中序遍历:递归法,迭代法
后序遍历:递归法,迭代法 - 广度优先遍历:
层序遍历:迭代法
二叉树的递归遍历
递归三步走:
- 确定函数参数和返回值
- 确定终止条件
- 确定单层递归逻辑
前序遍历
遍历顺序:根-左-右
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/
/**
* 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;
* }
* }
*/
class Solution {
public void Traversal(TreeNode node, List<Integer> result){
if(node == null){
return;
}
result.add(node.val); //根
Traversal(node.left, result); //左
Traversal(node.right, result); //右
}
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Traversal(root, result);
return result;
}
}
中序遍历
遍历顺序:左-根-右
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/
/**
* 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;
* }
* }
*/
class Solution {
public void Traversal(TreeNode node, List<Integer> result){
if(node == null){
return;
}
Traversal(node.left, result); //左
result.add(node.val); //根
Traversal(node.right, result); //右
}
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Traversal(root, result);
return result;
}
}
后序遍历
遍历顺序:左-右-根
链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/
/**
* 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;
* }
* }
*/
class Solution {
public void Traversal(TreeNode node, List<Integer> result){
if(node == null){
return;
}
Traversal(node.left, result); //左
Traversal(node.right, result); //右
result.add(node.val); //根
}
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Traversal(root, result);
return result;
}
}
二叉树的迭代遍历
前序遍历
遍历顺序:根-左-右
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if(root == null){
return result;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
result.add(cur.val); //根
if(cur.right != null){
stack.push(cur.right); //右
}
if(cur.left != null){
stack.push(cur.left); //左
}
}
return result;
}
}
中序遍历
遍历顺序:左-根-右
难点:节点访问顺序和处理顺序不同
不同点:中序遍历的写法不像前序遍历和后序遍历一样会在每次循环开始就更新cur的值,而是需要自己在 if 和 else 中手动更新cur的值
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode cur = root;
//cur != null 主要用于第一次进入循环
while(!stack.isEmpty() || cur != null){
//找到最左元素
if(cur != null){
stack.push(cur);
cur = cur.left;
}
//此时cur必为最左元素,将最左元素加入result
//将最左元素的右子树赋给cur,按同样规则遍历右子树
else{
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
//错误一
// if(cur.right != null){
// result.add(cur.right.val);
// }
//错误二
// if(cur.right != null){
// stack.push(cur.right);
// }
//两种错误写法都会导致cur不断将相同元素压入result/stack中,导致内存溢出
}
}
return result;
}
}
后序遍历
在前序遍历的基础上修改部分代码
遍历顺序:左-右-根
写法:根-左-右——>(调整代码顺序)根-右-左——>(反转)左-右-根
链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if(root == null){
return result;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
result.add(cur.val); //根
if(cur.left != null){
stack.push(cur.left); //左
}
if(cur.right != null){
stack.push(cur.right); //右
}
}
Collections.reverse(result);
return result;
}
}
二叉树的统一迭代遍历
关键:用null作为访问过但未处理过的根节点的标记
前序遍历
遍历顺序:根-左-右
入栈顺序:右-左-根
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if(root == null){
return result;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.peek();
if(cur != null){
stack.pop();
if(cur.right != null){
stack.push(cur.right); //右
}
if(cur.left != null){
stack.push(cur.left); //左
}
stack.push(cur);
stack.push(null); //根
}
else{
stack.pop();
cur = stack.pop();
result.add(cur.val);
}
}
return result;
}
}
中序遍历
遍历顺序:左-根-右
入栈顺序:右-根-左
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if(root == null){
return result;
}
stack.push(root);
while(!stack.isEmpty()){
//TreeNode cur = stack.pop(); //这里不可以直接pop,否则本应该在else分支里pop的null在这里就直接被pop了
TreeNode cur = stack.peek();
if(cur != null){ //null是用来标记根节点的,所以要保证除了自己手动加的null外没有其他的null入栈
stack.pop();
if(cur.right != null){
stack.push(cur.right); //右
}
stack.push(cur); //根
stack.push(null);
if(cur.left != null){
stack.push(cur.left); //左
}
}
else{
stack.pop(); //弹出null,准备处理根节点
cur = stack.pop();
result.add(cur.val);
}
}
return result;
}
}
后序遍历
遍历顺序:左-右-根
入栈顺序:根-右-左
链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if(root == null){
return result;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.peek();
if(cur != null){
stack.push(null); //根
if(cur.right != null){
stack.push(cur.right); //右
}
if(cur.left != null){
stack.push(cur.left); //左
}
}
else{
stack.pop();
cur = stack.pop();
result.add(cur.val);
}
}
return result;
}
}