随想录训练营Day14 | 二叉树理论基础, 三种遍历(递归法,迭代法)

88 阅读2分钟

随想录训练营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 二叉树的遍历

  1. 深度优先搜索---前中后序 递归法; 迭代法; 前序遍历: 中左右
  2. 广度优先搜索---层序搜索 迭代法;

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 指针指向正在遍历的元素;