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;
    • 可以通过前序遍历、中序遍历、后序遍历来遍历树,前序是根节点-左子树-右子树,中序是左子树-根节点-右子树,后序是左子树-右子树-根节点;
    • 删除一个节点只需要断开指向它的引用即可;
    • 哈夫曼树是二叉树,用于数据压缩算法,最经常出现的字符编码位数最少,很少出现的字符编码位数多一些。
  • 相关阅读:
    onmouseover事件
    ref:ThinkPHP Builder.php SQL注入漏洞(<= 3.2.3)
    ref:mysql命令大全
    ref:mysql丢失密码,如何修改?
    ref:学习笔记 UpdateXml() MYSQL显错注入
    转:[译]CSV 注入:被人低估的巨大风险
    转:深入剖析 JavaScriptCore
    转:Exploiting Electron RCE in Exodus wallet
    转:LNMP虚拟主机PHP沙盒绕过/命令执行(php exec命令被禁之后)
    转:Ubuntu16.04下配置php+vscode+xdebug开发环境
  • 原文地址:https://www.cnblogs.com/dtdx/p/13855599.html
Copyright © 2011-2022 走看看