zoukankan      html  css  js  c++  java
  • 数据结构与算法(四):树和二叉树

    什么是二叉树?

    树的定义

    (tree)是一种抽象数据类型(ADT),用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点通过连接它们的组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

    758472-20160420102124913-496312633

    树的相关概念

    1112095-20181127202628186-612767049

    ①、路径:顺着节点的边从一个节点走到另一个节点,所经过的节点的顺序排列就称为“路径”。

    ②、:树顶端的节点称为根。一棵树只有一个根,如果要把一个节点和边的集合称为树,那么从根到其他任何一个节点都必须有且只有一条路径。A是根节点。

    ③、父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;B是D的父节点。

    ④、子节点:一个节点含有的子树的根节点称为该节点的子节点;D是B的子节点。

    ⑤、兄弟节点:具有相同父节点的节点互称为兄弟节点;比如上图的D和E就互称为兄弟节点。

    ⑥、叶节点:没有子节点的节点称为叶节点,也叫叶子节点,比如上图的A、E、F、G都是叶子节点。

    ⑦、子树:每个节点都可以作为子树的根,它和它所有的子节点、子节点的子节点等都包含在子树中。

    ⑧、节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推。

    ⑨、深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;

    ⑩、高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;

    二叉树的定义

    如果了解了什么是树,那在这里二叉树也很好理解了。二叉树最基础的树类型结构,掌握二叉树的中序、前序、后序遍历方式,二叉树的插入和删除操作,为学习更复杂的二叉树打下扎实的基础

    二叉树(英语:Binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒。

    与普通树不同,普通树的节点个数至少为1,而二叉树的节点个数可以为0;普通树节点的最大分支度没有限制,而二叉树节点的最大分支度为2;普通树的节点无左、右次序之分,而二叉树的节点有左、右次序之分

    二叉树的特殊类型

    二叉树有两种特殊类型 满二叉树完全二叉树

    满二叉树

    满二叉树比较好理解,即一棵深度为k,且有{displaystyle 2^{egin{aligned}kend{aligned}}-1}个节点的二叉树,称为满二叉树(Full Binary Tree)。这种树的特点是每一层上的节点数都是最大节点数。

    如下图所示:

    fullBiTree

    满二叉树是是平衡的,左右对称,具备以下特点:

    1、所有叶子节点都在同一层。
    2、非叶子节点的度一定是 2 (节点的度表示该节点的子树个数)。
    3、同样深度的二叉树中,满二叉树的叶子节点最多。

    完全二叉树

    从概念上来讲,完全二叉树是指,如果对一棵有 n 个节点的二叉树按层次编号(从上到下,从左到右),如果任意编号 i 的节点与同样深度的满二叉树中编号为 i 的节点位置完全相同,则这棵树为完全二叉树。配合下图,可以更好的理解概念:

    entirelyTree

    所以满二叉树一定是完全二叉树,反过来则不一定是,完全二叉树具备以下特点:

    1、叶子节点只能出现在最下两层
    2、如果节点的度为 1,则该节点只能有左子树,不存在只有右子树的情况。

    完全二叉树用数组表示

    image-20201007164216091

    二叉树的遍历

    二叉树的遍历分为深度优先遍历(简称:DFS)和 广度优先遍历 (简称:BFS),其中 中序、前序、后序 三种遍历方式属于深度优先遍历,广度优先遍历需要借助队列按层遍历每个节点。

    深度优先遍历(DFS)

    二叉树的 中序遍历、前序遍历、后序遍历 都属于深度优先遍历。前中后说的是根节点的顺序,前序访问顺序:根 -> 左 -> 右;中序访问顺序:左 -> 根 -> 右;后序访问顺序:左 -> 右 -> 根;

    举个栗子:

    biTree01

    针对上边二叉树
    前序遍历:A B D E C F G
    中序遍历:D B E A F C G
    后序遍历:D E B F G C A

    下面是深度优先遍历相关代码:

    public class PrintTree {
    
        public static void main(String[] args) {
            BinaryTreeNode<String> root = new BinaryTreeNode("A");
            root.left = new BinaryTreeNode("B");
            root.right = new BinaryTreeNode("C");
            root.left.left = new BinaryTreeNode("D");
            root.left.right = new BinaryTreeNode("E");
            root.right.left = new BinaryTreeNode("F");
            root.right.right = new BinaryTreeNode("G");
    
            PrintTree printTree = new PrintTree();
            
            printTree.preOrderTree(root);
            System.out.println("前序遍历");
            printTree.inOrderTree(root);
            System.out.println("中序遍历");
            printTree.postOrderTree(root);
            System.out.println("后序遍历");
        }
    
        /**
         * 前序遍历:根 -> 左 -> 右
         *
         * @param root
         */
        public void preOrderTree(BinaryTreeNode root) {
            if (root == null) {
                return;
            }
            System.out.print(root.val + " ");
            preOrderTree(root.left);
            preOrderTree(root.right);
        }
    
        /**
         * 中序遍历:左 -> 根 -> 右
         *
         * @param root
         */
        public void inOrderTree(BinaryTreeNode root) {
            if (root == null) {
                return;
            }
            preOrderTree(root.left);
            System.out.print(root.val + " ");
            preOrderTree(root.right);
        }
    
        /**
         * 后序遍历:左 -> 右 -> 根
         *
         * @param root
         */
        public void postOrderTree(BinaryTreeNode root) {
            if (root == null) {
                return;
            }
            preOrderTree(root.left);
            preOrderTree(root.right);
            System.out.print(root.val + " ");
        }
    
        private static class BinaryTreeNode<T> {
    
            public T val;
            public BinaryTreeNode left;
            public BinaryTreeNode right;
    
            public BinaryTreeNode() {
            }
    
            public BinaryTreeNode(T value) {
                this.val = value;
            }
    
            public BinaryTreeNode(T val, BinaryTreeNode left, BinaryTreeNode right) {
                this.val = val;
                this.left = left;
                this.right = right;
            }
    
        }
    }
    

    广度优先遍历(BFS)

    广度优先遍历从根节点开始借助队列,一层层的遍历二叉树。

    如下图所示:

    image-20201007214322979

    广度优先遍历结果:A B C D E F G

    public class PrintTree {
    
        public static void main(String[] args) {
            BinaryTreeNode<String> root = new BinaryTreeNode("A");
            root.left = new BinaryTreeNode("B");
            root.right = new BinaryTreeNode("C");
            root.left.left = new BinaryTreeNode("D");
            root.left.right = new BinaryTreeNode("E");
            root.right.left = new BinaryTreeNode("F");
            root.right.right = new BinaryTreeNode("G");
    
            PrintTree printTree = new PrintTree();
    
            printTree.breadthOrderTree(root);
            System.out.println("广度优先遍历");
        }
    
        /**
         * 广度优先遍历
         * @param root
         */
        public void breadthOrderTree(BinaryTreeNode root){
            if (root == null){
                return;
            }
            Queue<BinaryTreeNode> queue = new LinkedList<BinaryTreeNode>();
            queue.add(root);
            while (!queue.isEmpty()){
                BinaryTreeNode node = queue.poll();
                System.out.print(node.val + " ");
                if (node.left != null){
                    queue.add(node.left);
                }
                if (node.right != null){
                    queue.add(node.right);
                }
            }
    
        }
    
        private static class BinaryTreeNode<T> {
    
            public T val;
            public BinaryTreeNode left;
            public BinaryTreeNode right;
    
            public BinaryTreeNode() {
            }
    
            public BinaryTreeNode(T value) {
                this.val = value;
            }
    
            public BinaryTreeNode(T val, BinaryTreeNode left, BinaryTreeNode right) {
                this.val = val;
                this.left = left;
                this.right = right;
            }
    
        }
    }
    

    总结

    • 树是由边和节点构成,根节点是树最顶端的节点,它没有父节点;
    • 二叉树中,最多有两个子节点;
    • 某个节点的左子树每个节点都比该节点的关键字值小,右子树的每个节点都比该节点的关键字值大,那么这种树称为二叉搜索树,其查找、插入、删除的时间复杂度都为logN;
    • 可以通过前序遍历、中序遍历、后序遍历来遍历树,前序是根节点-左子树-右子树,中序是左子树-根节点-右子树,后序是左子树-右子树-根节点;
    • 删除一个节点只需要断开指向它的引用即可;
    • 哈夫曼树是二叉树,用于数据压缩算法,最经常出现的字符编码位数最少,很少出现的字符编码位数多一些。
  • 相关阅读:
    2021.1.28 个人rating赛补题报告
    2021.1.23 个人rating赛补题报告
    2021.1.23 个人rating赛补题报告
    2020.12.14 个人训练赛补题报告
    2020.11.28 2020团体程序设计天梯赛补题报告
    2020.12.3 Codeforces Beta Round #73(Div2)补题报告
    Xhorse VVDI Prog V5.0.6 is Ready for BCM2 Adapter
    Program 2021 Ford Bronco All Keys Lost using VVDI Key Tool Plus
    Xhorse VVDI Prog V5.0.4 Software Update in July 2021
    How to use Xhorse VVDI2 to Exchange BMW FEM/BDC Module?
  • 原文地址:https://www.cnblogs.com/dtdx/p/13855599.html
Copyright © 2011-2022 走看看