代码随想录算法训练营第十四天 _ 二叉树_递归法和迭代法和统一迭代法分别实现前、中、后序遍历。

学习目标:

60天训练营打卡计划!

二叉树种类:

满二叉树

如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。

完全二叉树

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,且最底层的填充是从左到右填充。

二叉搜索树
  • 若左子树不为空,则左子树上所有的节点均小于他根节点的值。
  • 若右子树不为空,则右子树上所有的节点均大于他根节点的值。
平衡二叉搜索树(AVL树)
  • 对于树中的每一个节点,它的左子树和右子树的高度差不超过 1。
  • 对于树中的每一个节点,其左子树中的所有节点的值小于该节点的值,右子树中的所有节点的值大于该节点的值。

二叉树存储方式:

那么链式存储方式就用指针, 顺序存储的方式就是用数组。

数组存储二叉树

如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

二叉树遍历方式:

深度优先遍历(递归法,迭代法)

借助栈使用递归的方式来实现(只要记得住遍历的顺序即可

  • 前序遍历 左(子树)右(子树)
  • 中序遍历 左(子树) 右(子树)
  • 后序遍历 左(子树)右(子树)
广度优先遍历(迭代法)

一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。

  • 层序遍历
    上述的部分内容引用自代码随想录

学习内容:

递归法

144. 二叉树的前序遍历

  • java中新建数组的方法 :List res = new ArrayList();
class Solution {
    private void traversal(TreeNode cur, List<Integer> res){
        if(cur == null){
            return;
        }
        res.add(cur.val);
        traversal(cur.left, res);
        traversal(cur.right, res);
    }

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        traversal(root, res);
        return res;
    }
}

145. 二叉树的后序遍历

  • java中新建数组的方法 :List res = new ArrayList();
class Solution {
    private void traversal(TreeNode cur, List<Integer> res){
        if(cur == null){
            return;
        }
        traversal(cur.left, res);
        traversal(cur.right, res);
        res.add(cur.val);
    }

    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        traversal(root, res);
        return res;
    }
}

94. 二叉树的中序遍历

  • java中新建数组的方法 :List res = new ArrayList();
class Solution {
    private void traversal(TreeNode cur, List<Integer> res){
        if(cur == null){
            return;
        }
        traversal(cur.left, res);
        res.add(cur.val);
        traversal(cur.right, res);
    }

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        traversal(root, res);
        return res;
    }
}

迭代法(不是很熟悉)

整个过程可以分为访问节点和处理节点两个过程。其中,访问节点指的是将二叉树的节点放入栈的过程,而处理节点指的是将栈中的节点弹出到数组(打存储输出顺序的数组)的过程。

144. 二叉树的前序遍历

  • java中新建数组的方法 :List res = new ArrayList();
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root == null)
            return res;
        Stack<TreeNode> stk = new Stack<>();
        stk.push(root);
        while(!stk.empty()){
            // 每次先向数组中存入中间节点
            TreeNode node = stk.pop();
            res.add(node.val);
            // 再按照左子树和右子树的逆序
            // 先向数组中存放右节点(右子树的中间节点),
            // 再存放左节点(左子树的中间节点)。
            if(node.right != null){
                stk.push(node.right);
            }
            if(node.left != null){
                stk.push(node.left);
            }
        }
        return res;
    }
}

145. 二叉树的后序遍历

  • java中新建数组的方法 :List res = new ArrayList();
  • 和上述的前序遍历很相似,前序遍历是中间节点先出现,后序遍历是中间节点后出现。只要更换进栈顺序和数组翻转即可实现后序遍历。
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null){
            return res;
        }
        Stack<TreeNode> stk = new Stack<>();

        stk.push(root);
        // 入栈顺序:中-左-右 
        // 出栈顺序:中-右-左, 
        // 必须要先进行中间节点的处理。
        while(!stk.empty()){
            TreeNode node = stk.pop();
            res.add(node.val);
            if (node.left != null){
                stk.push(node.left);
            }
            if (node.right != null){
                stk.push(node.right);
            }
        }
        // 最后翻转结果
        Collections.reverse(res);
        return res;
    }
}

94. 二叉树的中序遍历

  • java中新建数组的方法 :List res = new ArrayList();
  • 分为访问节点和处理节点两个过程:
    一定会优先访问到中间节点,这样就没法实现左中右的顺序输出。
    指针从根节点一直向左子树遍历,遇到null就从栈中取元素,栈中存的是之前访问过的元素(中),就要处理该节点的右子节点。
/**
 * 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;
 *     }
 * }
 */
//  迭代法  当指针遇到树的null节点时就从栈中取元素
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        Stack<TreeNode> stk = new Stack<>();
        List<Integer> res = new ArrayList<>();
        TreeNode tmp = root;

        while(tmp != null || !stk.empty()){
            // 若节点左侧有节点,就向栈添加节点,并向左移动指针
            if(tmp != null){
                stk.push(tmp);
                tmp = tmp.left;
            } 
            // 若遇到节点为null,就从栈取值,操作,右移指针。
            else{
                tmp = stk.pop();
                res.add(tmp.val);
                tmp = tmp.right;
            }
        }
        return res;
    }
}

统一迭代的学习心得

将已访问但未处理的节点前加一个空指针。如图所示
请添加图片描述
下列的程序引用自

代码随想录pdf文件

迭代法前序遍历代码如下:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }
}

迭代法中序遍历代码如下:

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
    Stack<TreeNode> st = new Stack<>();
    if (root != null) st.push(root);
    while (!st.empty()) {
        TreeNode node = st.peek();
        if (node != null) {
            st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
            if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
            st.push(node);                          // 添加中节点
            st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。

            if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
        } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
            st.pop();           // 将空节点弹出
            node = st.peek();    // 重新取出栈中元素
            st.pop();
            result.add(node.val); // 加入到结果集
        }
    }
    return result;
}
}

迭代法后序遍历代码如下:

class Solution {
   public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)         
                               
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
   }
}

学习时间:

  • 上午半小时,下午两个半小时(实验室工作比较多),整理文档半小时。
  • 11月13日(day20)重新学习迭代法
  • 12月22日三刷,迭代法实现中序遍历仍不熟悉
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值