• Java数据结构之深度解析二叉树



    提示:以下是本篇文章正文内容,Java系列学习将会持续更新

    一、基本概念

    1-1 什么是二叉树?

     一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

    二叉树的特点
     每个结点最多有两棵子树,即二叉树不存在度大于 2 的结点。
     二叉树的子树有左右之分,其子树的次序不能颠倒,因此二叉树是有序树。

    1-2 两种特殊的二叉树

    在这里插入图片描述
    满二叉树的性质
     当树的高度为n时,共有(2^n - 1)个节点。
     第i层的节点数是:2^(n - 1)

    1-3 二叉树节点的存储

    二叉树的存储结构分为:顺序存储和类似于链表的链式存储。
    二叉树的链式存储是通过一个个的节点引用起来的,常见的表示方式有二叉和三叉表示方式。

    //孩子表示法
    class Node {
    	int val;   // 数据域
    	Node 1eft; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    //孩子双亲表示法
    class Node {
    	int val;   // 数据域
    	Node 1eft; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
    	Node parent; // 当前节点的根节点
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    回到目录…

    二、二叉树的递归操作

    /**
     * 基础二叉树的操作
     */
    public class MyBinTree {
        /**
         * 前序遍历:根左右
         * 传入一颗二叉树的根节点,就可以按照先序遍历的方式来输出节点值
         */
        public static void prevOrder(TreeNode root) {
            //边界条件
            if(root == null){
                return;
            }
            //先输出根节点
            System.out.print(root.val + " ");
            // 按照先序遍历的方式递归访问左子树
            prevOrder(root.left);
            // 按照先序遍历的方式递归访问右子树
            prevOrder(root.right);
        }
        //中序遍历:左根右
        public static void inOrder(TreeNode root) {
            if(root == null){
                return;
            }
            inOrder(root.left);
            System.out.print(root.val + " ");
            inOrder(root.right);
        }
        //后序遍历:左右根
        public static void postOrder(TreeNode root) {
            if(root == null){
                return;
            }
            postOrder(root.left);
            postOrder(root.right);
            System.out.print(root.val + " ");
        }
    
        /** 返回二叉树的节点数 **/
        public static int getNodes(TreeNode root) {
            if(root == null){
                return 0;
            }
            return 1 + getNodes(root.left) + getNodes(root.right);
        }
    
        /** 返回二叉树的叶子节点数 **/
        public static int getLeafNodes(TreeNode root) {
            if(root == null){
                return 0;
            }
            if(root.left == null && root.right == null){
                return 1;
            }
            return getLeafNodes(root.left) + getLeafNodes(root.right);
        }
    
        /** 判断该二叉树是否有val元素 **/
        public static boolean contains(TreeNode root, char val) {
            if(root == null){
                return false;
            }
            if(root.val == val){
                return true;
            }
            return contains(root.left,val) || contains(root.right,val);
        }
    
        /** 返回该二叉树的深度 **/
        public static int height(TreeNode root) {
            if(root == null){
                return 0;
            }
            return 1 + Math.max(height(root.left),height(root.right));
        }
    
        /** 返回该二叉树k层的节点数 **/
        public static int getKLevelNodes(TreeNode root,int k) {
            if(root == null || k<=0 || k > height(root)){
                return 0;
            }
            if(k == 1){
                return 1;
            }
            return getKLevelNodes(root.left,k-1) + getKLevelNodes(root.right,k-1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    回到目录…

    三、二叉树的迭代遍历

    例:在这里插入图片描述

    3-1 前序遍历 (prevOrder)

    根左右;结果集中,第一个遍历的节点就是根节点
    牛客地址

    public class Solution {
        public int[] preorderTraversal (TreeNode root) {
            if(root == null) {
                return new int[0];
            }
            List<Integer> list = new ArrayList<>();
            // 使用栈
            Deque<TreeNode> stack = new ArrayDeque<>();
            stack.push(root);
            while(!stack.isEmpty()){
                TreeNode cur = stack.pop();
                list.add(cur.val);
                // 因为栈先进后出,所以先压入右孩子
                if(cur.right != null){
                    stack.push(cur.right);
                }
                if(cur.left != null){
                    stack.push(cur.left);
                }
            }
            int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
            return arr;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    回到目录…

    3-2 中序遍历 (inOrder)

    左根右;左子树的遍历结果在根节点的左侧,右子树的遍历结果在根节点的右侧
    牛客地址

    public class Solution {
        public int[] inorderTraversal (TreeNode root) {
            if(root == null) {
                return new int[0];
            }
            List<Integer> list = new ArrayList<>();
            Deque<TreeNode> stack = new ArrayDeque<>();
            while(root != null || !stack.isEmpty()) {
                // 每次先找最左节点
                while(root != null){
                    stack.push(root);
                    root = root.left;
                }
                TreeNode node = stack.pop();
                list.add(node.val);
                // 进入右节点
                root = node.right;
            }
            return list.stream().mapToInt(Integer::intValue).toArray();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    回到目录…

    3-3 后序遍历 (postOrder)

    左右根;结果集中,最后一个遍历的节点就是根节点
    牛客地址

    public class Solution {
        public int[] postorderTraversal (TreeNode root) {
            if(root == null) {
                return new int[0];
            }
            List<Integer> list = new ArrayList<>();
            Deque<TreeNode> stack = new ArrayDeque<>();
            //当前节点
            TreeNode cur = root;
            //上一个完全处理过(左右根)的节点
            TreeNode prev = null;
            while(cur != null || !stack.isEmpty()){
                //先向左走到尽头
                while(cur != null){
                    stack.push(cur);
                    cur = cur.left;
                }
                //此时cur==null,让cur取栈顶元素
                cur = stack.pop();
                //判断右树是否为null或完全处理过的节点
                if(cur.right == null || prev == cur.right){
                    list.add(cur.val);
                    prev = cur;
                    cur = null;
                }else{
                    //此时右树不为空且未完全处理过,则继续处理右树
                    stack.push(cur);
                    cur = cur.right;
                }
            }
            return list.stream().mapToInt(Integer::intValue).toArray();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    回到目录…

    3-4 层序遍历 (levelOrder)

    逐层访问,从左到右
    牛客地址

    public class Solution {
        public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
            ArrayList<ArrayList<Integer>> order = new ArrayList<>(); 
            if(root == null){
                return order;
            }
            // 使用队列
            Queue<TreeNode> queue = new ArrayDeque<>();
            queue.offer(root);
            while(!queue.isEmpty()){
                // 统计当前层的元素个数
                int count = queue.size();
                // 建立链表存储当前层的元素
                ArrayList<Integer> list = new ArrayList<>();
                // 将当前层逐个出队加入链表中,并让它们的孩子入队
                for(int i = 0; i < count; i ++) {
                    TreeNode cur = queue.poll();
                    list.add(cur.val);
                    if(cur.left != null){
                        queue.offer(cur.left);
                    }
                    if(cur.right != null){
                        queue.offer(cur.right);
                    }
                }
                order.add(list);
            }
            return order;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    回到目录…


    总结:
    提示:这里对文章进行总结:
    以上就是今天的学习内容,本文是Java数据结构的学习,认识了二叉树,以及二叉树的相关操作(前序、中序、后序、层序遍历)。之后的学习内容将持续更新!!!

  • 相关阅读:
    NODEJS杂记
    Qt QSS基本属性样式表半通关
    Typora偏好设置中图床的配置文件点击打开没有反应
    一文讲清楚Java面向对象的继承关系
    C++进阶:二叉搜索树介绍、模拟实现(递归迭代两版本)及其应用
    Vue 3 Teleport:掌控渲染的艺术
    DAY60 84.柱状图中最大的矩形
    汇川Easy521PLC与压力传感器485通讯实例
    == 和 equals 的区别
    软文推广中媒体矩阵的优势在哪儿
  • 原文地址:https://blog.csdn.net/qq15035899256/article/details/126157324