• 数据结构--二叉树


    引言

    树是一种比较重要的数据结构,尤其是二叉树。在这里简单介绍二叉树。

    二叉树是一种特殊的树,在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子),并且二叉树的子树有左右之分,其次序不能任意颠倒。

    1. 二叉树

    1.1 二叉树的定义

    二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。

    如下是一棵普通的二叉树:

    1.3 斜树

    所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。

    如下是一棵右斜树:

    1.3 满二叉树

    定义:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

    如下是一棵满二叉树:

    1.4 完全二叉树

    定义:对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。

    如下是一棵完全二叉树:

    完全二叉树的特点:

    1. 叶子结点只能出现在最下层和次下层。
    2. 最下层的叶子结点集中在树的左部。
    3. 倒数第二层若存在叶子结点,一定在右部连续位置。
    4. 如果结点度为1,则该结点只有左孩子,即没有右子树。
    5. 同样结点数目的二叉树,完全二叉树深度最小。

    【区别】

    也就是说,在满叉树的基础上,我在最底层从右往左删去若干节点,得到的都是完全二叉树。

    所以说,满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树

    1.5 二叉树的使用场景

    普通的二叉树,很难构成现实的应用场景,但因其简单,常用于学习研究,平衡二叉树则是实际应用比较多的。常见于快速匹配、搜索等方面。

    常用的树有:AVL树、红黑树、B/B+树、Trie(字典)树

    • AVL树:最早的平衡二叉树之一。应用相对其他数据结构比较少。windows对进程地址空间的管理用到了AVL树。
    • 红黑树:集合map和set都是用红黑树实现的。还有Linux文件管理。
    • B/B+树:用在磁盘文件组织 数据索引和数据库索引。
    • Trie树(字典树): 用在统计和排序大量字符串,如自动机、M数据库索引。

    2. 二叉树的遍历

    2.1 存储方式

    二叉树的存储分为顺序存储链式存储 。顺序存储在右斜树这种极端情况下,会十分浪费存储空间,顺序存储结构一般适用于完全二叉树。因此二叉树的存储一般会使用链式存储,在这里只介绍链式存储:

    二叉树的链式存储结构,将节点的数据结构定义为一个数据域和两个指针域,如下图所示:

    则完整的二叉树可以表示为:

    对于节点的声明,使用java代码可如下声明:

    public class TreeNode {
        String data;
        TreeNode LChild;
        TreeNode RChild;
        TreeNode(String data) {
            this.data = data;
        }
    }
    

    2.2 遍历方式

    二叉树的遍历是指从二叉树的根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。

    二叉树的遍历方式可以分为四种:

    • 前序遍历:root >> left >> right
    • 中序遍历:left >> root >> right
    • 后续遍历:left >> right >> root
    • 层序遍历:按照层次遍历

    对于如上图二叉树的遍历结果为:

    前序遍历:ABCDEFGHK

    中序遍历:BDCAEHGKF

    后序遍历:DCBHKGFEA

    前序遍历就是从二叉树的根结点出发,当第一次到达结点时就输出结点数据,按照先向左在向右的方向访问。

    中序遍历就是从二叉树的根结点出发,当第二次到达结点时就输出结点数据,按照先向左在向右的方向访问。

    后序遍历就是从二叉树的根结点出发,当第三次到达结点时就输出结点数据,按照先向左在向右的方向访问。

    3. 代码实现

    3.1 定义二叉树节点存储结构

    class TreeNode {
            String root;
            TreeNode LChild;
            TreeNode RChild;
    
            TreeNode(String root) {
                this.root = root;
            }
    
            public String toString() {
                return root;
            }
        }
    

    3.2 构建二叉树

    对于二叉树的构建,使用前序遍历构建比较合适也较为简单。但是在构建的时候要使二叉树左右子树都有数据,如下图为要构建的二叉树:

    在构建时要表示为如下的二叉树:

    对于阴影的部分我们使用 ”.“ 表示,即以上的二叉树我们可用数组表示为:

    String[] tree = {"A", "B", ".", "C", "D", ".", ".", ".", "E", ".", "F", "G","H",".", ".","k",".",".","."};
    
    

    3.3 完整代码

    public class TreeTest {
    
        class TreeNode {
            String root;
            TreeNode LChild;
            TreeNode RChild;
    
            TreeNode(String root) {
                this.root = root;
            }
    
            public String toString() {
                return root;
            }
        }
    
        private static String[] install = {"A", "B", ".", "C", "D", ".", ".", ".", "E", ".", "F", "G","H",".", ".","k",".",".","."};
        private static int i = 0;
        //使用前序遍历构建二叉树
        TreeNode createBTree() {
            TreeNode treeNode = null;
            String data = install[i++];
            if (data.equals(".")) {
                return treeNode;
            } else {
                treeNode = new TreeNode(data);
                treeNode.LChild = createBTree();
                treeNode.RChild = createBTree();
                return treeNode;
            }
        }
    
    
        //前序遍历
        void preOrderTree(TreeNode tree) {
            if (tree != null) {
                System.out.print(tree + " ");
                preOrderTree(tree.LChild);
                preOrderTree(tree.RChild);
            }
        }
    
        //中序遍历
        void inOrderTree(TreeNode tree){
            if (tree != null) {
                inOrderTree(tree.LChild);
                System.out.print(tree + " ");
                inOrderTree(tree.RChild);
            }
        }
    
        //后序遍历
        void afterOrderTree(TreeNode tree){
            if (tree != null) {
                afterOrderTree(tree.LChild);
                afterOrderTree(tree.RChild);
                System.out.print(tree + " ");
            }
        }
    
        //层序遍历
        public int TreeDepth(TreeNode tree) {
            if(tree == null){
                return 0;
            }
            int left = TreeDepth(tree.LChild);
            int right = TreeDepth(tree.RChild);
            return (left > right) ? (left+1) : (right+1);
        }
    
        //测试
        public static void main(String[] args) {
            TreeTest test = new TreeTest();
            TreeNode bTree = test.createBTree();
            test.preOrderTree(bTree);	//A B C D E F G H k 
    
            System.out.println();
            test.inOrderTree(bTree);	//B D C A E H G k F 
    
            System.out.println();
            test.afterOrderTree(bTree);	//D C B H k G F E A 
    
            System.out.println();
            int depth = test.TreeDepth(bTree);
            System.out.println(depth);	//5
        }
    
    }
    
  • 相关阅读:
    Cookie和Seesion
    Forms组件
    分页器组件
    关于Django的Ajax操作
    Oracle常用数据库表操作
    redis的缓存穿透 缓存并发 缓存失效
    Struts2的拦截器
    Struts2的各种标签库
    Struts2基础知识
    Java常用数据结构和算法
  • 原文地址:https://www.cnblogs.com/luler/p/13986342.html
Copyright © 2020-2023  润新知