数据结构之二叉树(Binary Tree)详解

news2024/11/27 10:25:57

目录

1、什么是二叉树?

2、二叉树的遍历:深度优先和广度优先

(1)深度优先搜索(DFS)算法

(2)广度优先搜索(BFS)算法

3、二叉树的性质详解

4、二叉树的类型

(1)满二叉树

(2)完全二叉树

(3)二叉搜索树/二分搜索树

(4)平衡二叉树

(5)线段树


1、什么是二叉树?

        二叉树是一种树型数据结构,其中每个节点最多可以有两个子节点,分别称为左子节点和右子节点。// 时间复杂度跟树的深度有关

        下面是一个具有整数数据的树节点示例:

class Node {
    int key;
    Node left, right;
 
    public Node(int item)
    {
        key = item;
        left = right = null;
    }
}

        使用 Java 代码构造一棵二叉树:

public class BinaryTree {

    // Root of Binary Tree
    Node root;

    // Constructors
    BinaryTree(int key) {
        root = new Node(key);
    }

    BinaryTree() {
        root = null;
    }

    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();

        // 创建根节点
        tree.root = new Node(1);
        /* 下面是上述语句后的树
           1
          / \
        null null
        */

        tree.root.left  = new Node(2);
        tree.root.right = new Node(3);
        /* 2和3是1的左右子结点
              1
             / \
            2   3
           / \ / \
       null null null null */
        tree.root.left.left = new Node(4);
        /* 4变成了2的左子结点
               1
              / \
             2   3
            / \ / \
           4 null null null
          / \
        null null
        */
    }

    private static class Node {

        int key;
        Node left, right;

        public Node(int item) {
            key  = item;
            left = right = null;
        }
    }
}

2、二叉树的遍历:深度优先和广度优先

        不像线性数据结构(数组,链表,队列,堆栈等)只有一种逻辑方式来遍历它们,树可以用不同的方式遍历。

(1)深度优先搜索(DFS)算法

        深度优先遍历(Depth-First Traversal)是一种针对二叉树的遍历方式,它的核心思想是尽可能深地访问二叉树的节点,直到无法继续深入为止,然后回溯到上一层节点继续遍历。深度优先遍历有三种常见的方式:前序遍历、中序遍历后序遍历// 本质就是先访问根节点还是后访问根节点

        1)前序遍历(Preorder Traversal)

        前序遍历的规则是先访问根节点,然后按照从左到右的顺序递归地遍历左子树和右子树。前序遍历的顺序是:根节点 -> 左子树 -> 右子树

        前序遍历的代码实现:

public class BinaryTree {

    // 根节点
    Node root;

    BinaryTree() {
        root = null;
    }

    // 给定一棵二叉树,按顺序打印它的节点
    void printPreorder(Node node) {
        if (node == null) {
            return;
        }
        // 打印当前节点的数据
        System.out.print(node.key + " ");
        // 遍历左子树
        printPreorder(node.left);
        // 遍历右子树
        printPreorder(node.right);
    }

    // Driver code
    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.root            = new Node(1);
        tree.root.left       = new Node(2);
        tree.root.right      = new Node(3);
        tree.root.left.left  = new Node(4);
        tree.root.left.right = new Node(5);

        // Function call
        System.out.println("traversal of binary tree is ");
        tree.printPreorder(tree.root);
    }

    private static class Node {

        int  key;
        Node left, right;

        public Node(int item) {
            key  = item;
            left = right = null;
        }
    }
}

//输出
traversal of binary tree is 
1 2 4 5 3 

        2)中序遍历(Inorder Traversal)

        中序遍历的规则是先按照从左到右的顺序递归地遍历左子树,然后访问根节点,最后再递归遍历右子树。中序遍历的顺序是:左子树 -> 根节点 -> 右子树

        中序遍历在二叉搜索树中具有特别重要的应用,因为中序遍历会按照节点的值从小到大的顺序遍历。

        中序遍历的代码实现:// 只需调整上边代码中的遍历顺序即可

    // 给定一棵二叉树,按顺序打印它的节点
    void printInorder(Node node) {
        if (node == null) {
            return;
        }
        // 遍历左子树
        printInorder(node.left);
        // 打印当前节点的数据
        System.out.print(node.key + " ");
        // 遍历右子树
        printInorder(node.right);
    }


//输出
traversal of binary tree is 
4 2 5 1 3 

        3)后序遍历(Postorder Traversal)

        后序遍历的规则是先按照从左到右的顺序递归地遍历左子树和右子树,然后访问根节点。后序遍历的顺序是:左子树 -> 右子树 -> 根节点

        后序遍历的代码实现:// 只需调整上边代码中的遍历顺序即可

    // 给定一棵二叉树,按顺序打印它的节点
    void printPostorder(Node node) {
        if (node == null) {
            return;
        }
        // 遍历左子树
        printPostorder(node.left);
        // 遍历右子树
        printPostorder(node.right);
        // 打印当前节点的数据
        System.out.print(node.key + " ");
    }

//输出
traversal of binary tree is 
4 5 2 3 1 

(2)广度优先搜索(BFS)算法

        二叉树的广度优先遍历(Breadth-First Traversal),也称为层次遍历(Level Order Traversal),是一种按照层级顺序逐层访问二叉树节点的遍历方式。从根节点开始,按照从左到右的顺序依次访问每一层的节点,直到遍历完所有节点。// 按层读取

        层序遍历的代码实现:

public class BinaryTree {

    // 根节点
    Node root;

    BinaryTree() {
        root = null;
    }

    /*层序遍历*/
    void printLevelOrder() {
        int h = height(root);
        int i;
        for (i = 1; i <= h; i++) {
            printCurrentLevel(root, i);
        }
    }

    /* 计算树的高度 -- 从根节点开始沿着最长路径的节点一直到最远的叶节点.*/
    int height(Node root) {
        if (root == null) {
            return 0;
        } else {
            /* 计算每个子树的高度 */
            int lheight = height(root.left);
            int rheight = height(root.right);

            /* use the larger one */
            if (lheight > rheight) {
                return (lheight + 1);
            } else {
                return (rheight + 1);
            }
        }
    }

    /* Print nodes at the current level */
    void printCurrentLevel(Node root, int level) {
        if (root == null) {
            return;
        }
        if (level == 1) {
            System.out.print(root.key + " ");
        } else if (level > 1) {
            printCurrentLevel(root.left, level - 1);
            printCurrentLevel(root.right, level - 1);
        }
    }

    // Driver code
    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.root            = new Node(1);
        tree.root.left       = new Node(2);
        tree.root.right      = new Node(3);
        tree.root.left.left  = new Node(4);
        tree.root.left.right = new Node(5);

        // Function call
        System.out.println("traversal of binary tree is ");
        tree.printLevelOrder();
    }

    private static class Node {

        int  key;
        Node left, right;

        public Node(int item) {
            key  = item;
            left = right = null;
        }
    }
}

//输出
traversal of binary tree is 
1 2 3 4 5 

        四种遍历方法遍历下面的树:

  1. 前序遍历结果:1-2-4-5-3-6-7
  2. 中序遍历结果:4-2-5-1-6-3-7
  3. 后序遍历结果:4-5-2-6-7- 1
  4. 层序遍历结果:1-2-3-4-5-6-7

3、二叉树的性质详解

        二叉树示例图://注意二叉树并没有元素的大小顺序,仅仅只是规定子节点数为2

        (1)性质1:二叉树的第 i 层上至多有 2^(i-1)(i≥1)个节点// 每层的节点

  • 如果是第 1 层(根节点),那么该层最多有 2^(1-1),也就是 2^0 = 1 个节点。
  • 如果是第 3 层,那么该层最多有 2^(3-1),也就是 2^2 = 4 个节点。

        (2)性质2深度为 h 的二叉树中至多含有 2^h - 1 个节点// 整棵树的节点

  • 如果树的深度为 1(只有根节点),整棵树最多有 2^1-1,也就是 2 - 1 = 1 个节点。
  • 如果树的深度为 3 ,整棵树最多有 2^3 - 1,也就是 8 - 1 = 7 个节点。

        (3)性质3:若在任意一棵二叉树中,有 n0 个叶子节点,有 n2 个度为 2 的节点,则必有 n0 = n2 + 1。

        如二叉树示例图,在图中的叶子节点数为 4 个(没有子节点的节点),分别为:2、5、11、4,所以 n0 = 4;度为 2 的节点有 3 个,分别为:2、7、6;则总有:n0 = n2 + 1,在上图所述的二叉树中也就是:3 + 1 = 4  // 有点像叶子节点数量等于前边所有父节点数量之和 + 1(满二叉树)

        // 补充:什么是二叉树的度?

        二叉树的度是指树中所有节点的度数的最大值。1 度就代表只有一个子节点或者它是单子树, 2 度就代表有两个子节点或是左右子树都有,二叉树的度小于等于 2,因为二叉树的定义要求二叉树中任意节点的度数(节点的分支数)小于等于 2 。// 节点个数的最大值

        (4)性质4:具有 n 个节点的满二叉树的深度为 [log(2)n] + 1。([ ]表示向下取整)

        如下图,一颗满二叉树有 15 个节点,那么 [log(2) 15] = 3,那么这棵二叉树的深度为:3 + 1 = 4。

        log(2)n 的计算方式示例:根据 2^4 = 16,可得 log(2) 16 = 4。 

        // 补充:什么是满二叉树和完全二叉树?

        满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。

        完全二叉树:一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。// 满二叉树的缩小版

        (5)性质5:若对一棵有 n 个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为 i(i≥1)的节点:

  • 当 i = 1 时,该节点为根,它无双亲节点。
  • 当 i > 1 时,则结点 i 的父结点编号为 int_down(i/2)  // int_down 表示向下取整,比如 5 的父节点为 2 ->  int_down( 5 /2) = 2
  • 若 2i ≤ n,则有编号为 2i 的左节点,否则没有左节点。// 下图中的 2、4、6 节点
  • 若 2i + 1 ≤ n,则有编号为 2i + 1 的右节点,否则没有右节点。// 下图中的 3、5 节点

4、二叉树的类型

(1)满二叉树

        满二叉树(Full Binary Tree),也称为严格二叉树,是一种特殊的二叉树。在满二叉树中,除了叶子节点外,每个节点都有两个子节点,并且所有的叶子节点都位于同一层上

        满二叉树具有以下性质:

  1. 所有的非叶子节点都有两个子节点。
  2. 所有的叶子节点都在同一层上。
  3. 深度为 h 的满二叉树,总共有 2^h - 1 个节点。

        下面是一棵满二叉树的结构:

(2)完全二叉树

        完全二叉树(Complete Binary Tree)是一种特殊的二叉树结构,在完全二叉树中,除了最后一层的叶子节点可能不满外,其他层的节点都是满的,并且最后一层的叶子节点都连续地靠左排列。

        若设二叉树的深度为 h ,那么除第 h 层外,其它各层的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边。

        下面是展示了一个完全二叉树的结构:

        满二叉树是完全二叉树的特殊形态,如果一棵二叉树是满二叉树,则它必定是完全二叉树。

(3)二叉搜索树/二分搜索树

        二叉搜索树(Binary Search Tree,BST)是一种有序的二叉树数据结构,其中对于任意节点,其左子树中的节点值都小于该节点的值,其右子树中的节点值都大于该节点的值。简而言之,二叉搜索树满足以下性质:

        对于任意节点N:

  • 其左子树中的所有节点的值都小于N的值。
  • 其右子树中的所有节点的值都大于N的值。
  • 其左子树和右子树也都是二叉搜索树。

        这个性质使得二叉搜索树具有以下特点:

  1. 二叉搜索树中左子树中的节点值小于根节点的值,右子树中的节点值大于根节点的值,因此在二叉搜索树中查找特定值的操作可以通过比较节点值大小来进行二分搜索,提高查找效率
  2. 二叉搜索树的中序遍历结果是有序的,即按照升序排列

        以下是一个二叉搜索树的结构:

        由于二叉搜索树的有序性质,它可以用作高效的数据存储和检索结构。例如,在数据库中,使用二叉搜索树可以加速查找、插入和删除操作,提高数据的访问效率。// 二分搜索,后续将详细介绍

(4)平衡二叉树

        平衡二叉树(Balanced Binary Tree)是一种特殊的二叉树结构,其目的是在插入和删除节点时保持树的平衡,以提高搜索、插入和删除等操作的效率。// 主要是降低树的高度,防止二叉树退化成链表

        平衡二叉树具有以下性质:

  1. 对于任意节点,其左子树和右子树的高度差不超过1,即左子树和右子树的高度之差的绝对值不超过1。
  2. 每个子树都是平衡二叉树。

        平衡二叉树的常见实现包括AVL树红黑树。这些树在插入或删除节点时会自动进行平衡操作,保持树的平衡性。平衡操作包括左旋、右旋、双旋等,通过调整节点的位置和平衡因子,使得树的高度差保持在可接受的范围内。

        平衡二叉树的平衡性质保证了树的高度较低,从而使得搜索、插入和删除等操作的时间复杂度保持在O(log n)级别。相比于非平衡的二叉搜索树,平衡二叉树在大规模数据集和频繁动态操作的场景中具有更好的性能。

        然而,平衡二叉树的维护需要付出额外的代价,例如在插入和删除节点时进行平衡操作可能会导致较高的复杂性。因此,在某些特定的应用场景中,可能会选择其他更适合的数据结构,如B树跳表,以平衡性能和复杂性的需求。

        以下是一个AVL树的结构:// AVL树后续会在其他文章详细介绍,此处只做简介

        以下是一个红黑树的结构:// AVL树后续会在其他文章详细介绍,此处只做简介

        红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对之进行平衡的代价较低, 其平均统计性能要强于 AVL 树。

        重要性质:从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点// 黑节点平衡

(5)线段树

        线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。

        线段树是将每个区间 [L,R] 分解成 [L,M] 和 [M+1,R]  (其中 M = (L+R) / 2 这里的除法是整数除法,即对结果下取整),直到  L == R 为止。 

        开始时是区间 [1,n],通过递归来逐步分解,假设根的高度为 1 的话,树的最大高度为(n>1)。 

        线段树对于每个 n 的分解是唯一的, 所以 n 相同的线段树结构相同,这也是实现可持久化线段树的基础。

        下图展示了区间 [1,13] 的分解过程:

        上图中,每个区间都是一个节点,每个节点存自己对应的区间的统计信息。

        总结:

        线段树的核心思想是将给定的区间划分为多个较小的子区间,并在每个节点上维护与该区间相关的信息。通常情况下,线段树是一棵平衡二叉树,每个叶节点表示原始数据中的一个单独元素,而其他节点表示其对应区间的聚合信息。

        线段树的主要优点是可以在O(log n)的时间复杂度内进行区间查询和更新操作。通过利用线段树的特殊结构,可以快速定位并操作涉及的区间。线段树常被用于解决各种区间查询问题,如区间最值查询、区间修改、区间覆盖等。

        关于线段树的推荐阅读文章,点击《线段树》。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615695.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IT服务台追踪的关键故障指标

指标是 IT 服务管理的核心&#xff0c;可提供运营见解并帮助确定持续改进的领域。通常的服务台指标有助于展示内部运营效率。为 例如&#xff0c;衡量在指定时间内解决的工单数量的 SLA 是展示服务台效率的关键因素。另一方面&#xff0c;故障指标可帮助团队识别 IT 基础架构中…

Linux基本指令学习(入门)

Linux基本指令学习 0.在xshell中登录自己的虚拟机1. ls指令2. pwd命令3. cd 指令4. touch指令5.mkdir指令6.rmdir指令 && rm 指令7.man指令&#xff1a;8.cp指令9.mv指令&#xff1a;10.cat11.more指令12.less指令13.head指令14.tail指令15.时间相关的指令16.Cal指令17…

【Linux】Tomcat 部署及优化

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 Tomcat 部署及优化 一、Tomcat 概述1.Tomcat 介绍2.Tomcat 核心组件3.Tomcat 组件结构4.Tomcat 处理请求过程 二、Tomcat 部署步骤1.关闭防火墙2.安装JDK3.设置JDK环境变量4.…

SQL基础培训16-日期处理

进度16-日期处理-SQL基础培训 知识点(学习作业就是按示例练习一遍): 1、系统当前日期 select getdate() as 当前系统日期 --建表时&

Antd时间轴Timeline遍历数据

Antd时间轴遍历数据 <Timelineitems{data.map((item) > {return { children: item };})}/>

Java实现手动操作定时任务功能的简单例子(动态创建定时任务)

还记得在前公司与同事共同开发一个在页面上统一管理定时提醒任务的功能&#xff0c;实际业务可能会复杂一些&#xff0c;它需要结合小程序或公众号平台对各个用户进行周期性&#xff08;按季度、按月、按日等&#xff09;的消息推送。由于我当时负责的是小程序和公众号平台方面…

node18 + express + mongoose 开发后台服务踩坑记录

模块化 1、node 默认采用 common.js 模块化规范; 2、在 node 中使用 es module 时&#xff0c;需将 package.json 中的 type 改为 module。或者将 .js 文件改为 .mjs。 发布全局 npm cli &#xff08;自定义 npm 脚手架&#xff09; 3、创建一个 npm 脚手架&#xff0c;需创…

Delaunay三角剖分学习笔记

文章目录 Delaunay三角剖分学习笔记1 Voronoi \text{Voronoi} Voronoi图1.1 定义与性质 2 三角剖分2.1 定义与性质2.2 质量(quality)评定标准 3 Delaunay三角剖分3.1 定义3.2 准则与性质 4 Delaunay三角剖分算法4.1 Bowyer-Watson算法4.1.1 算法步骤&#xff1a;4.1.2 算法伪代…

学会这样写代码,一看就是资深工程师,代码简洁之道PHP版本

文章目录 一、前言二、规范2.1 整体结构规范2.1.1 类的括号前括号单独一行2.1.2 方法的前括号单独一行2.1.3 方法内部语句前括号不换行 2.2 变量与常量2.2.1 变量的命名尽量要有语义2.2.2 同一个实体要用相同的变量名2.2.3 尽量使用有语义的常量表述2.2.4 使用自解释型变量2.2.…

六面钻调试第八步Cam参数调试(一)之压轮压板 ,机械规格调试

Cam参数调试 &#xff08;1&#xff09;.压轮压板参数调试 注&#xff1a;压板的规格测量设置 压板的位置相对基准钻的位置设置 &#xff08;2&#xff09;机械规格调试 1.定位气缸的类型 2.活动式定位气缸的Y向宽度 4.定位杆与夹钳Y边缘的最小间隙 5.活动式定位气缸相对基准…

CVPR视频理解论文

Learning Transferable Spatiotemporal Representations from Natural Script Knowledge 时空TransformerCLIP的对比学习思路

if/while/for/语句/分支/路径覆盖的控制流程图+数据流分析(DU)

if/while/for/语句覆盖/分支覆盖/路径覆盖的控制流程图数据流分析(DU) 语句的线性序列Linear Sequences of Statements “If” Constructs “While” Constructs “For” Constructs 语句覆盖率Statement Coverage 测试套件应执行 CFG 的所有节点 也被称为&#xff1a;…

为什么客服系统必备知识库?提高客服效率的秘密武器!

对任何想要成功的企业而言&#xff0c;客户服务是必不可少的。依据提供的客户支持质量&#xff0c;可建立或破坏一个品牌的声誉。为了提供高标准的客户服务&#xff0c;企业必须能够获得可靠的信息&#xff0c;并能够为用户提供快速有效的解决方案。下面&#xff0c;我们就详细…

matlab学习1--基础

文章目录 基本语法保存和加载数组/矩阵矩阵运算 输出多个值绘制向量图 基本语法 和弱语言一样&#xff0c;变量赋值不需要声明类型直接 a 1 2命名规则 以字母开头&#xff0c;并且仅包含字母、数字和下划线 (_) 区分大小写清除命令行窗口 clc保存和加载 保存 保存到xxx.…

python开发构建轻量级卷积神经网络模型实现手写甲骨文识别系统

手写汉字、手写数字、手写字母识别模型都已经做过很多了&#xff0c;但是手写甲骨文识别这个应该都是很少有听说过的吧&#xff0c;今天也是看到这个数据集就想着基于这批手写甲骨文数据集开发构建识别模型&#xff0c;首先来看下效果图&#xff1a; 接下来看下对应使用的数据集…

字典的学习笔记

列表 [] 单身什么是字典 {} 二人世界 python内置的数据结构之一&#xff0c;与列表一样是一个可变序列(可以增删改操作的) 以键值对的方式存储数据&#xff0c;字典是一个无序的序列 -> hash(key) 通过哈希函数来计算存储位置,key一定是不可变的字典的创建 使用花…

读论文-MHFormer

论文&#xff1a;Multi-hypothesis representation learning for transformer-based 3D human pose estimation 摘要 尽管取得了重大进展&#xff0c;但由于深度模糊和自遮挡&#xff0c;从单目视频中估计3D人体姿势仍然是一项具有挑战性的任务。大多数现有的作品都试图通过利用…

驱动程序接口和HAL层区别和联系

驱动程序接口&#xff08;Device Driver Interface&#xff09;和硬件抽象层&#xff08;Hardware Abstraction Layer&#xff0c;HAL&#xff09;是在软件系统中用于处理硬件的两个不同层次的概念。 驱动程序接口&#xff08;Device Driver Interface&#xff09;&#xff1a;…

设计模式(二、三):创建型之工厂方法和抽象工厂模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 目录 一、…

校园综合能效平台建设的意义

摘 要&#xff1a;为响应国家绿色校园建设的号召&#xff0c;切实提高高校能源利用水平&#xff0c;促进学校能源资源合理配置&#xff0c;服务学校高质量发展大局&#xff0c;根据教育部印发的《关于开展节能减排学校行动的通知》《关于勤俭节约办教育建设节约型校园的通知》…