代码随想录第16天有感:二叉树

本文详细介绍了二叉树的基本构造,包括节点定义、节点度的概念,以及满二叉树、完全二叉树、二叉搜索树和平衡二叉搜索树的区别。此外,文章还覆盖了深度优先遍历(前序、中序、后序)和广度优先遍历(层序)的方法,以及如何计算最大深度和最小深度。
摘要由CSDN通过智能技术生成

二叉树的基本构造

public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    TreeNode() {}
    public TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

二叉树术语

,“度”指的是一个节点拥有的子节点的数量。在一颗二叉树中,一个节点的度可以是0、1或2:

- 度为0:表示该节点是一个叶子节点,即它没有子节点。
- 度为1:表示该节点有一个子节点,可以是一个左子节点或一个右子节点。
- 度为2:表示该节点有两个子节点,既有左子节点又有右子节点。

二叉树的类型

当然,这四种二叉树在数据结构中有着不同的定义和特性:

1. 满二叉树(Full Binary Tree)
   满二叉树是一种特殊的二叉树,其中每个节点要么没有子节点(即度为0,是叶子节点),要么有两个子节点(即度为2)。这意味着在满二叉树中不存在只有一个子节点的节点。另外,满二叉树的所有叶子节点都位于同一层级。

2. 完全二叉树(Complete Binary Tree)
   完全二叉树是介于满二叉树和平衡二叉树之间的一种形态。在完全二叉树中,所有的层级都被完全填满,除了最后一层。在最后一层上,所有的节点都尽可能地向左对齐。这意味着如果最后一层不完整,那么未填满的部分一定在该层的右侧。

3. 二叉搜索树(Binary Search Tree,BST)
   二叉搜索树是一种特殊的二叉树,对于树中的每个节点,其左子树上的所有节点的值都小于它的节点值,其右子树上的所有节点的值都大于它的节点值。二叉搜索树支持高效的节点查找、插入和删除操作。

4. 平衡二叉搜索树(Balanced Binary Search Tree)
   平衡二叉搜索树是一种特殊的二叉搜索树,其中任何节点的两个子树的高度差不超过1。这种高度平衡保证了树的操作(如查找、插入、删除)在最坏情况下都能保持对数时间复杂度。AVL树和红黑树是平衡二叉搜索树的两个例子,它们通过旋转等操作来维护树的平衡。

二叉树的遍历方式

深度优先遍历(Depth-First Search, DFS)

  • 前序遍历(Pre-order Traversal) 在前序遍历中,遍历的顺序是“根-左-右”。首先访问根节点,然后遍历左子树,最后遍历右子树。

  • 中序遍历(In-order Traversal) 中序遍历的顺序是“左-根-右”。首先遍历左子树,然后访问根节点,最后遍历右子树。对于二叉搜索树,中序遍历的结果是按键值升序访问树中所有节点的遍历方式。

  • 后序遍历(Post-order Traversal) 后序遍历的顺序是“左-右-根”。首先遍历左子树,然后遍历右子树,最后访问根节点。

广度优先遍历(Breadth-First Search, BFS)

  • 层序遍历(Level-order Traversal) 层序遍历从树的根开始,按照层级从上到下,每层从左到右的顺序逐个访问所有节点。这通常是通过使用队列来实现的。

二叉树中的深度和高度

深度(Depth)

根节点到任意结点的距离(按层序遍历划分,如根节点为1,则它的左右子树为2),每当向下访问树的下一层,则距离加1

高度(Height)

任意结点到叶子结点的距离(按层序遍历划分,如叶子结点为1,则它的根节点为2),每当向上访问树的上一层,则距离加1

什么是最大深度
 

即根结点到叶子结点的最远距离(也就是根节点到最底层的叶子节点的距离)。注意:对一颗二叉来说,求它的最大深度也就相当于求整棵树的高度(即根节点的高度)。解法:即可以使用前序遍历,也可以使用后序遍历。
 

什么是最小深度

即根节点到它第一个为null的子节点的距离(这里的子节点包含根节点的左右结点,左右结点的孩子)

二叉树的基本遍历实现

前序遍历
public static void main(String[] args) {
        TreeNode node = new TreeNode(1);
        node.left = new TreeNode(2);
        node.left.left = new TreeNode(3);
        node.left.right = new TreeNode(4);
        node.right = new TreeNode(2);
        node.right.right = new TreeNode(3);
        node.right.left = new TreeNode(4);
        levelOrderTraversal(node).forEach(System.out::print);
        System.out.println();
    }
    /**
     * 什么是DP Dynamic Programming
     * 一种优化问题求解方法,通常用于解决具有 重叠子问题 和 最优子结构 性质的问题
     * 基本思想是将原问题分解成更小的子问题,通过求解和保存这些子问题的解,避免重复计算,从而提高算法的效率
     */

    /**
     * 实现二叉树的前序遍历。
     *
     * @param root 二叉树的根节点
     * @return
     */
    public static List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        //递归
        preorderDP(root, res);
        //迭代
        preorder(root);
        return res;
    }

    public static void preorderDP(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        res.add(root.val);
        preorderDP(root.left, res);
        preorderDP(root.right, res);
    }

    /**
     * 前序遍历 中 左 右
     * 入栈顺序 中 右 左
     * 出栈顺序 中 左 右
     *
     * @param root
     * @return
     */
    public static List<Integer> preorder(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            res.add(node.val);
            //根据栈的特性,先进后出;所以先将右节点压栈,再将左节点压栈
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return res;
    }
中序遍历
/**
     * 实现二叉树的中序遍历。
     *
     * @param root 二叉树的根节点
     * @return
     */
    public static List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorderDP(root, res);
        return res;
    }

    public static void inorderDP(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorderDP(root.left, res);
        res.add(root.val);
        inorderDP(root.right, res);
    }

    /**
     * 中序遍历 左 中 右
     * 入栈顺序 左 右
     *
     * @param root
     * @return
     */
    public static List<Integer> inorder(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                res.add(cur.val);
                cur = cur.right;
            }
        }

        return res;
    }
后序遍历
/**
     * 实现二叉树的后序遍历。
     *
     * @param root 二叉树的根节点
     * @return
     */
    public static List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorderDP(root, res);
        return res;
    }

    public static void postorderDP(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        postorderDP(root.left, res);
        postorderDP(root.right, res);
        res.add(root.val);
    }


    /**
     * 后序遍历 左 右 中
     * 入栈顺序 中 左 右
     * 出栈顺序 中 右 左
     * 集合反转 左 右 中
     *
     * @param root
     * @return
     */
    public static List<Integer> postorder(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            res.add(node.val);
            if (node.left != null) {
                stack.push(node.left);
            }
            if (node.right != null) {
                stack.push(node.right);
            }
        }
        Collections.reverse(res);
        return res;
    }
层序遍历
/**
     * 实现二叉树的层序遍历。
     *
     * @param root 二叉树的根节点
     * @return
     */

    public static List<Integer> levelOrderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                TreeNode node = queue.poll();
                res.add(node.val);
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
        }
        return res;
    }

二叉树题目

题目:104. 二叉树的最大深度

即求根节点的高度

/**
     * @param root 二叉树的根节点
     * @return 计算二叉树的最大深度
     */
    public static int maxDeep(TreeNode root) {
        //后序遍历
        if (root == null) {
            return 0;
        }
        int leftDeep = maxDeep(root.left);//左
        int rightDeep = maxDeep(root.right);//右
        return Math.max(leftDeep, rightDeep) + 1;//中
    }

    public static int deep = 0;

    public static int maxDeepByLevel(TreeNode root) {
        if (root == null) {
            return 0;
        }
        //层序遍历
        level(root, 0);
        return deep;
    }

    public static void level(TreeNode root, int index) {
        //递归的遍历左右子树的最大深度
        //到达叶子节点后,更新深度deep
        if (root == null) {
            deep = Math.max(deep, index);
            return;
        }
        index++;
        level(root.left, index);
        level(root.right, index);
    }
最小深度

求根节点的最小深度,注意如果根节点的左右子树任意为null,则返回的是1,如下图左侧计算

 /**
     * @param root 二叉树的根节点
     * @return 计算二叉树的最小深度,注意如果根节点的左右子树任意为null,则返回的是1
     */
    public static int minDeep(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDeep = maxDeep(root.left);
        int rightDeep = maxDeep(root.right);
        return Math.min(leftDeep, rightDeep) + 1;
    }
题目:111. 二叉树的最小深度

题目中的最小深度指的是:从根节点到最近叶子节点的最短路径上的节点数量。如上图右侧计算

思路:

分别计算左子树和右子树的最大深度,即leftDeeprightDeep。如果左子节点为空而右子节点不为空,说明根节点的左子树为空,那么最小深度就是右子树的最大深度加1。同样,如果右子节点为空而左子节点不为空,最小深度就是左子树的最大深度加1。

最后,函数返回1加上左右子树中的最小深度,即1 + Math.min(leftDeep, rightDeep)
 

/**
     * @param root 二叉树的根节点
     * @return 计算二叉树的最小深度,排除如果根节点的左右子树任意为null,则返回的是1的情况
     * 即从根节点到最浅叶子节点的最小深度
     */
    public static int minDeepLogin(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDeep = maxDeep(root.left);
        int rightDeep = maxDeep(root.right);
        if (root.left == null && root.right != null) {
            return rightDeep + 1;
        }
        if (root.right == null && root.left != null) {
            return leftDeep + 1;
        }
        return 1 + Math.min(leftDeep, rightDeep);
    }

  • 21
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值