Java数据结构与算法分析(七)二叉树

简介: 二叉树是一棵特殊的树,其结构简单但很重要。二叉树的特点是每个节点最多有两棵子树,并且有左右之分。

GitHub源码分享

项目主页:https://github.com/gozhuyinglong/blog-demos
本文源码:https://github.com/gozhuyinglong/blog-demos/tree/main/java-data-structures

1. 二叉树(Binary Tree)

二叉树是一棵特殊的,其结构简单但很重要。二叉树的特点是每个节点最多有两棵子树,并且有左右之分。

  • 满二叉树
    如果一棵二叉树的所有叶子节点都在最后一层,称为满二叉树。满二叉树的结点总数 = $2^n-1$ (n为层数)。如下图二叉是的层数为3,其结点总数为$2^3-1=7$
    满二叉树
  • 完全二叉树
    一棵深度为k的有n个结点的二叉树,对树中的节点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。显示下图中a和b是完全二叉树,而c不是完全二叉树(倒数第二层不连续)

完全二叉树

小结:一棵满二叉树一定是一棵完全二叉树;而一棵完全二叉树不一定是满二叉树。

2. 二叉树的五种形态

二叉树是递归定义的,其节点有左右子树之分,逻辑上二叉树有五种基本形态:

二叉树的五种形态

  • 空二叉树(图a)
  • 只有一个根节点的二叉树(图b)
  • 只有左子树(图c)
  • 只有右子树(图d)
  • 完全二叉树(图e)

3. 二叉树的遍历(前序、中序、后序)

  • 前序遍历:先输出父节点,再遍历左子树,最后遍历右子树。
  • 中序遍历:先遍历左子树,再输出父节点,最后遍历右子树。
  • 后序遍历:先遍历左子树,再遍历右子树,最后输出父节点。

小结:用输出父节点的顺序来判断是前序、中序还是后序遍历

4. 代码实现

通过Java代码实现下图中二叉树,并通过三种方式遍历该二叉树(前序、中序、后序)。

二叉树

Node类为节点类,其中element表示节点元素,left为左子节点,right为右子节点。

BinaryTree类实现二叉树的具体操作,如前序遍历、中序遍历、后序遍历。

public class BinaryTreeDemo {
   
   

    public static void main(String[] args) {
   
   

        BinaryTree tree = new BinaryTree();

        Node a = new Node("A");
        Node b = new Node("B");
        Node c = new Node("C");
        Node d = new Node("D");
        Node e = new Node("E");
        Node f = new Node("F");
        Node g = new Node("G");
        Node h = new Node("H");
        Node i = new Node("I");

        tree.setRoot(a);
        a.left = b;
        a.right = c;
        b.left = d;
        b.right = e;
        d.left = h;
        c.left = f;
        c.right = g;
        g.right = i;

        System.out.println("---------------前序遍历");
        tree.preOrder();
        System.out.println("---------------中序遍历");
        tree.inOrder();
        System.out.println("---------------后序遍历");
        tree.postOrder();

    }

    private static class BinaryTree {
   
   

        private Node root; // 根

        public void setRoot(Node root) {
   
   
            this.root = root;
        }

        /**
         * 前序遍历
         */
        public void preOrder() {
   
   
            preOrder(root, 0);
        }

        /**
         * 前序遍历
         *
         * @param node
         * @param depth 层级(用于辅助输出)
         */
        public void preOrder(Node node, int depth) {
   
   

            if (node == null) {
   
   
                return;
            }

            // 输出当前节点
            this.print(node, depth);

            // 递归左子节点
            if (node.left != null) {
   
   
                preOrder(node.left, depth + 1);
            }

            // 递归右子节点
            if (node.right != null) {
   
   
                preOrder(node.right, depth + 1);
            }

        }

        /**
         * 中序遍历
         */
        public void inOrder() {
   
   
            inOrder(root, 0);
        }

        /**
         * 中序遍历
         *
         * @param node
         * @param depth 层级(用于辅助输出)
         */
        public void inOrder(Node node, int depth) {
   
   

            if (node == null) {
   
   
                return;
            }

            // 递归左子节点
            if (node.left != null) {
   
   
                inOrder(node.left, depth + 1);
            }

            // 输出当前节点
            this.print(node, depth);

            // 递归右子节点
            if (node.right != null) {
   
   
                inOrder(node.right, depth + 1);
            }

        }

        /**
         * 后序遍历
         */
        public void postOrder() {
   
   
            postOrder(root, 0);
        }

        /**
         * 后序遍历
         *
         * @param node
         * @param depth 层级(用于辅助输出)
         */
        public void postOrder(Node node, int depth) {
   
   

            if (node == null) {
   
   
                return;
            }

            // 递归左子节点
            if (node.left != null) {
   
   
                postOrder(node.left, depth + 1);
            }

            // 递归右子节点
            if (node.right != null) {
   
   
                postOrder(node.right, depth + 1);
            }

            // 输出当前节点
            this.print(node, depth);

        }

        /**
         * 按照层级输出节点元素
         *
         * @param node
         * @param depth
         */
        private void print(Node node, int depth) {
   
   
            StringBuilder t = new StringBuilder();
            for (int i = 0; i < depth; i++) {
   
   
                t.append("\t");
            }
            System.out.printf("%s%s\n", t.toString(), node.element);
        }
    }

    private static class Node {
   
   
        private final Object element; // 节点元素
        private Node left; // 左子节点
        private Node right; // 右子节点

        public Node(Object element) {
   
   
            this.element = element;
        }
    }
}

输出结果:

---------------前序遍历
A
    B
        D
            H
        E
    C
        F
        G
            I
---------------中序遍历
            H
        D
    B
        E
A
        F
    C
        G
            I
---------------后序遍历
            H
        D
        E
    B
        F
            I
        G
    C
A
相关文章
|
3月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
55 1
|
9天前
|
存储 算法 Python
文件管理系统中基于 Python 语言的二叉树查找算法探秘
在数字化时代,文件管理系统至关重要。本文探讨了二叉树查找算法在文件管理中的应用,并通过Python代码展示了其实现过程。二叉树是一种非线性数据结构,每个节点最多有两个子节点。通过文件名的字典序构建和查找二叉树,能高效地管理和检索文件。相较于顺序查找,二叉树查找每次比较可排除一半子树,极大提升了查找效率,尤其适用于海量文件管理。Python代码示例包括定义节点类、插入和查找函数,展示了如何快速定位目标文件。二叉树查找算法为文件管理系统的优化提供了有效途径。
40 5
|
8天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
21天前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
37 5
|
2月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
93 4
|
2月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
2月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
53 6
|
2月前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
66 5
|
2月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
143 8
|
2月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。