Java【二叉树】相关知识+基本操作

news2024/11/30 10:33:04

文章目录

  • 前言
  • 一、树形结构
    • 1.树的定义
    • 2.树的概念(术语)
    • 3.树的存储结构
  • 二、二叉树
    • 1.二叉树的概念
      • 两种特殊的二叉树
    • 2.二叉树的性质
    • 3.二叉树的存储结构
    • 4.二叉树的基本操作
      • 4.1 二叉树的前序遍历--Preorder Traversal
      • 4.2 二叉树的中序遍历--Inorder Traversal
      • 4.3 二叉树的后序遍历--Postorder Traversal
      • 4.4 二叉树的层序遍历--levelOrde
      • 4.5 获取树中节点的个数 -- size
      • 4.6 获取叶子节点的个数 -- getLeafNodeCount
      • 4.7 获取第K层节点的个数 -- getKLevelNodeCount
      • 4.8 获取二叉树的高度 -- getHeight
      • 4.9 检测值为value的元素是否存在 -- find
  • 总结


前言

前几篇文章介绍了数据结构中线性结构:顺序表,链表,栈,队列,并进行了模拟实现,理解了线性结构的原理和相关知识。但是我们知道,线性结构都是一对一的关系,如果处理一对多的情况,就需要使用树形结构

树形结构包括:二叉树,VAl树,B树,红黑树… 今天主要介绍 二叉树 的相关知识,在此之前要先了解什么是树形结构

二叉树是VAl树,B树,红黑树… 的基础, 学好了二叉树才能在后续学习更难的树时得心应手


提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎评论区指点~ 废话不多说,直接发车~

一、树形结构

1.树的定义

《大话数据结构》中对树这样定义:
树( Tree ) 是 n ( n >= 0 ) 个结点的有限集。n=0 时称为空树,在任意一棵非空树中:
(1) 有且仅有 1 个特定的称为 根( Tree ) 的结点
(2) n>1 时,其余结点可分为 ( m>0 ) 个互不相变的有限集T1 、T2、……、Tm, 其中每一个集合本身又是一棵树,并且称为 根的子树( SubTree )

啥意思?直接看图:
在这里插入图片描述

注意
1,有且仅有一个根结点
2,正因为子树的存在,子树T1也是树,所以 树形结构是递归定义的
3,子树之间不能相交——如图所示:
在这里插入图片描述

子树之间不能相交,所以得出结论:
1,除根结点外,一个结点只有一个父结点
2,一棵树有N个结点,则有N-1条边


2.树的概念(术语)

在这里插入图片描述

必会:
结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6

树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6

叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I…等绿色的节点为叶结点

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点;如上图:B是A的孩子结点

根结点:一棵树中,没有双亲结点的结点;如上图:A

结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推

树的高度或深度:树的层数; 如上图:树的高度为4

了解:
非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G…等节点为分支结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为堂兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先, E 是 I, Q 的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由m(m>=0)棵互不相交的树组成的集合称为森林


3.树的存储结构

线性表中,存储结构都相对简单,但树形结构的存储结构比较复杂,并且有很多种:双亲表示法,孩子表示法、孩子双亲表示法、孩子兄弟表示法等等,这里先了解即可


二、二叉树

1.二叉树的概念

二叉树,顾名思义就是 一个结点最多有两个叉 ,两个叉分别叫 左子树右子树, 既然被称作左右子树, 那么也是一棵树
二叉树的每一个结点都可以当作一棵二叉树的根节点, 所以二叉树十分满足递归的特点

如图:
在这里插入图片描述

那么每一个结点的度最多为2,二叉树的结点可以只有一个叉,可以是“左叉”,也可以是“右叉”,如图:
在这里插入图片描述
但需要注意:如果这个结点没有 “左叉” 只有 “右叉” ,那么“右叉”也称为右子树,而不能称为左子树,因为二叉树是有序树


两种特殊的二叉树

1,满二叉树
大白话解释:每一个子树都“长满了叉”,不存在只有一个叉的结点,如图:
在这里插入图片描述
右边那棵树有四层, 但第四层没有长发育好, 第三层的最右边叶子结点没有左右子树, 它不是满二叉树

2,完全二叉树
大白话解释:结点可以只有一个叉,但不能只有 “右叉” 而没有 “左叉” ,或者有左叉而没有右叉时, 右边还有结点, 如图:
在这里插入图片描述
每一棵树按从左往后从上到下的顺序标号, 每一个结点对应一个下标时就是完全二叉树
右边那棵树的10,11下标处没有结点, 但12,13下标有结点, 它就不是完全二叉树

堆这种结构就是顺序存储的完全二叉树


2.二叉树的性质

若规定根结点的层数为1,则一棵非空二叉树的第 i 层上最多有2^(i-1) 个结点(i>0)

若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^k - 1个(k>=0)

对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1

具有n个结点的完全二叉树的深度k为 log₂(n+1)上取整

对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为 i 的结点有:
1.若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
2.若2i+1<n,左孩子序号:2i+1,否则无左孩子
3.若2i+2<n,右孩子序号:2i+2,否则无右孩子
这一点了解即可, 后续分享 [优先级队列 — 堆] 相关内容时会再介绍


3.二叉树的存储结构

二叉树的存储结构分为顺序存储和链式存储, 一般主要以链式存储为主, 以顺序结构存储的二叉树称作 "堆 "

二叉树的链式存储结构就像链表那样, 是由一个个结点链接而成, 每个结点中有三个域: 一个值域和两个指针域 , 两个指针域分别是这个结点的左右子树的地址
回顾链表的模拟实现方式, 每个结点都是 binaryTree 这个类的内部类, 所以上述的三个域就是三个内部类的成员属性

binaryTree 还需要一个成员属性 root 来记录二叉树的根节点

链式存储主要以孩子表示法为主

public class binaryTree {
    private static class treeNode {
        private char val;
        private treeNode left;
        private treeNode right;

        public treeNode(char val) {
            this.val = val;
        }
    }
    private treeNode boot;
}

4.二叉树的基本操作

4.1 二叉树的前序遍历–Preorder Traversal

在这里插入图片描述

前序遍历是指, 从根节点开始, 先访问根节点, 再访问左子树, 再访问右子树, 对于每一颗树来说都是这样的访问顺序. 简称 "根左右" 的顺序

这个顺序对每一颗树来说都相同: 那么当二叉树的根节点访问完之后应该访问这个根结点的左子树, 那么这棵左子树也是树, 同样需要按照 “根左右” 的顺序访问…这就是递归, 来看图解:
在这里插入图片描述
前序遍历序列: ABDEGCF

每次递归都要递归到叶子结点为止, 而这个叶子结点是没有左右子树的, 所以递归的终止条件就是 root == null
来看代码如何实现:;

   public void preorderTraversal(treeNode root) {
        if (root == null) {
            return;
        }
        System.out.println(root.val);
        prevOrder(root.left);
        prevOrder(root.right);
    }

代码非常简单, 只需要写出递归的终止条件, 然后先打印, 再把这个结点的左子树传参,再把这个结点的右子树传参


上述写法的思路是遍历每一个结点并访问, 属于遍历思想, 而二叉树的学习中还有子问题思想是经常需要用到的

如果把 preorderTraversal() 这个方法的返回值改成 List< Integer > 应该怎么写呢?
应该很简单, 只需要给一个 List 对象, 把刚刚的打印操作改成 add 操作即可:

   public List<Integer> list = new ArrayList<>();
   public List<Integer> preorderTraversal(treeNode root) {
        if (root == null) {
            return list;
        }
        list.add(root.val);
        prevOrder(root.left);
        prevOrder(root.right);
        return list
    }

这样的写法仍然是遍历思想, 只是一个一个结点的, 把访问的方式从打印变成 add

请注意, 在上述代码中, 左右递归时,好像并没有接收返回值呀, 明明有返回值, 但应该如何接收呢 ? 或者说, 接收返回值能有什么用呢?
对于二叉树的每一个结点来说, 都可以当作一棵树的根结点, 我们需要返回一整棵树的前序遍历得到的 List , 那么就可以想, 如果在 list 中 add 上根节点自己的 val 值, 再 add 上根节点的左右子树的 List 中的每一个值, 不就得到整颗树的 List 了吗


   public List<Integer> preorderTraversal(treeNode root) {
     	public List<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        list.add(root.val);
        List<Integer> leftList = prevOrder(root.left);
        list.addAll(leftList);// 把左树的List集合全部add到list中
        List<Integer> rightList = prevOrder(root.right);
        list.addAll(rightList);// 把右树的List集合全部add到list中
        return list
    }

这样一来, 返回值也用到了, 并且是标准的子问题思想

对于返回值是 List 集合这种前序遍历来说:
遍历思想的解决方式是 — 遇到一个结点就 add
子问题思想的解决方式是 — add 根节点 + add 左子树所有节点 + add 右子树所有节点

这里只是先提一下子问题思想的方式, 后面还有很多地方要用到子问题思想, 熟练掌握运用这种思想也不是一蹴而就的, 需要日积月累的锻炼


4.2 二叉树的中序遍历–Inorder Traversal

在这里插入图片描述

中序遍历是指, 从根节点开始, 先访问左子树, 再访问根节点, 再访问右子树, 对于每一颗树来说都是这样的访问顺序. 简称 "左根右" 的顺序

这个顺序对每一颗树来说都相同: 先访问二叉树的根节点的左子树, 那么这棵左子树也是树, 同样需要按照 “根左右” 的顺序访问…当左子树全部访问完后, 返回到父节点并访问,然后再访问父节点的右子树…

利用子问题思想也是可以写出来的噢

中序遍历序列:DBGEACF

递归思想和前序遍历是一致的, 那么代码中只需要更改一下打印结点的位置即可:

   public void inorderTraversal(treeNode root) {
        if (root == null) {
            return;
        }
        prevOrder(root.left);
        System.out.println(root.val);
        prevOrder(root.right);
    }

利用子问题思想也是可以写出来的噢


4.3 二叉树的后序遍历–Postorder Traversal

在这里插入图片描述

后序遍历是指, 从根节点开始, 先访问左子树, 再访问右子树, 再访问根节点, 对于每一颗树来说都是这样的访问顺序. 简称 "左右根" 的顺序

这个顺序对每一颗树来说都相同: 先访问二叉树的根节点的左子树, 那么这棵左子树也是树, 同样需要按照 “根左右” 的顺序访问…当左子树全部访问完后, 再访问父节点的右子树…同样需要按照 “根左右” 的顺序访问…当左右子树都访问完后, 再返回到父节点并访问

后序遍历序列: DGEBFCA

递归思想和前序遍历中序遍历是一致的, 那么代码中只需要更改一下打印结点的位置即可:

   public void postorderTraversal(treeNode root) {
        if (root == null) {
            return;
        }
        prevOrder(root.left);
        prevOrder(root.right);
        System.out.println(root.val);
    }

4.4 二叉树的层序遍历–levelOrde

在这里插入图片描述
层序遍历非常简单, 就是看二叉树有几层, 每层从左往右的顺序访问即可

层序遍历序列: ABCDEFG

层序遍历没有递归的思想,而是利用循环就可以做到, 需要借助一个队列, 先让根结点入队, 然后当队列不为空时循环, 让队列中的结点依次出队,令它为top , 如果 top 有左右孩子结点就让左右孩子结点入队, 循环结束代表每个结点都访问过了

    public void levelOrder(treeNode root){
        if(root == null) {
            return;
        }
        Queue<treeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            treeNode node = queue.poll();
            System.out.println(node.value + " ");
            if(node.left != null) {
                queue.offer(node.left);
            }
            if(node.right != null) {
                queue.offer(node.right);
            }
        }

4.5 获取树中节点的个数 – size

已经学习过了各种遍历, 求节点个数就很简单了, 只需要定义一个变量 count , 在遍历到每一个结点时, 让count++即可
这是遍历思想的实现, 不再赘述代码, 主要介绍子问题思想的实现:

问题是求所有结点的个数, 那么把这个问题的所有子问题就是 : 求根节点的个数 + 左子树所有结点的个数 + 右子树所有结点的个数
根节点个数肯定就是1, 那么二叉树的叶子节点算不算一棵树的根节点呢?
肯定算, 不过这棵树只有根节点, 没有左右子树而已

    public int size(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftSize = size(root.left);
        int rightSize = size(root.right);
        return leftSize + rightSize + 1;
    }

4.6 获取叶子节点的个数 – getLeafNodeCount

同样, 用子问题思想的解决方法是: 左树的叶子节点个数 + 右树叶子节点的个数

    int getLeafNodeCount(TreeNode root) {
        if(root == null) {
            return 0;
        }
        if(root.left == null && root.right == null){
            return 1;
        }
        int leftSize = getLeafNodeCount(root.left);
        int rightSize = getLeafNodeCount(root.right);
        return leftSize+rightSize;
    }

4.7 获取第K层节点的个数 – getKLevelNodeCount

同样, 用子问题思想的解决方法是: 左树的第 K 层结点个数 + 右树第 K 层的个数
如何求第 K 层结点个数呢? 反正 root 都会递归遍历到每一层的每一个结点, 只需要定义一个遍历 K, root 每走一层就让 K - 1, 当 K 等于 1 时,root 所在的这一层就是第 K 层, 返回个数(1)即可

    int getKLevelNodeCount(TreeNode root,int k) {
        if(root == null) {
            return 0;
        }
        if(k == 1) {
            return 1;
        }
        int leftSize = getKLevelNodeCount(root.left,k-1);
        int rightSize = getKLevelNodeCount(root.right,k-1);
        return leftSize + rightSize;
    }

4.8 获取二叉树的高度 – getHeight

首先明确一个问题: 高度为1的树是什么样子? 只有一个结点, 没有左右子树

之前的子问题思路都是左子树的…加上右子树的…, 现在的问题是 求二叉树的高度, 那么这就要涉及到左右子树的高度的比较了
对于满二叉树来说, 根节点的左右子树高度肯定是一样的, 那其他情况呢? 左右子树高度不平衡, 这就需要先求出左子树高度, 再求出右子树高度, 如果左子树比右子树高就让左子树+1, 如果右子树比左子树高就让右子树+1

哎? 不对啊,为啥还要给高的那棵树再+1呢?
再看一遍原话, 是先求 左右树的高度, 是树!!

比如开学第一天,你的老师让你统计一下全班的人数, 你把所有人都查了一遍, 结果忘了算你自己

我们把左右子树的较高的那棵树找到之后, 再+1 就是整棵树的高度, 所以
+1, 加的是根节点本身的高度

    public int getHeight(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);

        return (leftHeight > rightHeight) ?
                (leftHeight+1):(rightHeight+1);
    }

4.9 检测值为value的元素是否存在 – find

先往左子树找, 如果找到了就不去右子树, 直接返回, 如果没找到就去右子树找, 找到了就返回, 如果还没找到就是真没找到

    TreeNode find(TreeNode root, int val) {
        if(root == null) {
            return null;
        }
        if(root.val == val) {
            return root;
        }
        TreeNode leftTree = find(root.left,val);
        if(leftTree != null) {
            return leftTree;
        }
        TreeNode rightTree = find(root.right,val);
        if(rightTree != null) {
            return rightTree;
        }
        return null;//没有找到
    }

总结

以上就是二叉树的基本知识, 介绍了相关概念和性质, 以及二叉树的基本操作, 还有一个问题没解决: 二叉树如何构建, 下篇文章会收录二叉树的相关OJ题, 其中就有关于二叉树的构建, 以及其他较难的问题

如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~


上山总比下山辛苦
下篇文章见

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

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

相关文章

Vue项目中如何使用computed计算属性

computed: 1.基本使用 在computed中定义一个函数&#xff08;看起来是一个函数&#xff0c;其实是一个属性&#xff09;&#xff0c;命名按照属性规范命名(一般为名词)。 1.1 应用场景&#xff1a; 当数据A的逻辑很复杂时&#xff0c;把A这个数据写在计算属性里面 1.2 代码位置…

星戈瑞收藏Sulfo-CY7 amine/NHS ester/maleimide小鼠活体成像染料标记反应

关于小鼠活体成像&#xff0c;就一定要提到CY活性染料标记反应&#xff1a; 用不同的活性基团的Cyanine菁染料和相应的活性基团的生物分子或小分子药物发生反应&#xff0c;链接到一起。 根据需要标记的抗原、抗体、酶、多肽等分子所带的可标记基团的种类&#xff08;氨基、醛…

Java中GSON的使用(内含源代码)

Java中GSON的使用&#xff08;内含源代码&#xff09; 源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87474951 目录Java中GSON的使用&#xff08;内含源代码&#xff09;源代码下载链接地址&#xff1a;[https://download.csdn.net/dow…

智能遥测终端机 水文RTU 5G水资源监测 雨水情测报应用

M21F-V50系列智能遥测终端机是辰迈智慧自主研发的融合北斗三代短报文、4/5G公网通信、LoRa、Bluetooth、Ethernet等多种通信方式、数据采集及防盗报警于一体的智能遥测终端。产品内置数据采集器&#xff0c;实现内部模数转换&#xff0c;提供增量式雨量接口、RS232、RS485、以太…

【07-JVM面试专题-JVM运行时数据区的虚拟机栈你知道吗?它的基本结构是什么呢?你知道栈帧的结构吗?那你说说动态链接吧?】

JVM运行时数据区的虚拟机栈你知道吗&#xff1f;它的基本结构是什么呢&#xff1f;你知道栈帧的结构吗&#xff1f;那你说说动态链接吧&#xff1f; JVM运行时数据区的虚拟机栈你知道吗&#xff1f;它的基本结构是什么呢&#xff1f;你知道栈帧的结构吗&#xff1f;那你说说动态…

itk中的一些图像处理

文章目录1.BinomialBlurImageFilter计算每个维度上的最近邻居平均值2.高斯平滑3.图像的高阶导数 RecursiveGaussianImageFilter4.均值滤波5.中值滤波6.离散高斯平滑7.曲率驱动流去噪图像 CurvatureFlowImageFilter8.由参数alpha和beta控制的幂律自适应直方图均衡化9.Canny 边缘…

React hooks之useContext《类比Vue的Provide》(三)

系列文章目录 提示&#xff1a;我又来更新啦&#xff01;&#xff01;&#xff01;这次又把之前useEffect的补充了一次&#xff01;&#xff01;&#xff01;&#xff01; 文章目录系列文章目录前言1.Vue中的Provide和useContext&#xff08;相同点&#xff09;2..Vue中的Provi…

大数据Hadoop教程-学习笔记02【Apache Hadoop、HDFS】

视频教程&#xff1a;哔哩哔哩网站&#xff1a;黑马大数据Hadoop入门视频教程教程资源&#xff1a;https://pan.baidu.com/s/1WYgyI3KgbzKzFD639lA-_g 提取码: 6666【P001-P017】大数据Hadoop教程-学习笔记01【大数据导论与Linux基础】【P018-P037】大数据Hadoop教程-学习笔记0…

0301微分中值定理-微分中值定理与导数的应用

文章目录1 罗尔定理2 拉格朗日定理3 柯西中值定理5后记1 罗尔定理 费马引理 设函数f(x)在点x0f(x)在点x_0f(x)在点x0​的某邻域U(x0)U(x_0)U(x0​)内有定义&#xff0c;并且在点x0x_0x0​处可导&#xff0c;如果对任意的x∈U(x0),x\in U(x_0),x∈U(x0​),有 f(x)≤f(x0)(或f(x)…

给你的Vim加上自动编程功能

Vim 是高度可定制、可扩展的编辑器。这对热爱折腾的程序员来说&#xff0c;绝对是一种乐趣&#xff0c;同时也是进一步提升开发效率的源泉。近期CodeGeeX举办的黑客松活动中&#xff0c;参赛选手Lance 提交了一款名叫Vim with CodeGeeX的作品&#xff0c;这款作品是基于CodeGee…

Java开源工具库使用之httpclient

文章目录前言一、简单使用1.1 get 请求1.2 post 简单表单请求1.3 表单上传文件1.4 上传 json 数据二、高级用法2.1 超时和重试2.2 Cookie2.3 拦截器2.4 fluent API三、3.1旧版本使用3.1 Get 请求3.2 Post 请求四、异步版本使用4.1 基本请求4.2 请求流水线执行参考前言 HttpCli…

SSM SpringBoot vue 健康医疗预约系统

SSM SpringBoot vue 健康医疗预约系统 SSM 健康医疗预约系统 功能介绍 首页 图片轮播展示 出诊信息推荐 医院公告 科室信息 科室详情 出诊信息 医生详情 挂号预约 登录注册 留言反馈 个人中心 我的收藏 后台管理 登录 注册医生 个人中心 用户管理 科室信息管理 医生管理 出诊…

二、Java虚拟机的基本结构

Java虚拟机的架构1.内存结果概述2.类加载器子系统的作用3. 类加载器ClassLoader角色4.类的加载过程5.类加载器的分类1.引导类加载器(虚拟机自带的加载器)Bootstrap ClassLoader2.扩展类加载器(虚拟机自带的加载器) Extenssion ClassLoader3.应用程序类加载器(虚拟机自带的加载器…

CAN TP层函数介绍

如果想使用CAN TP层函数,首先需要在网络节点或测试节点配置页面的Componets组件一栏添加osek_tp.dll文件。路径为:C:\Program Files\Vector CANoe 15\Exec32 至于节点的CAPL程序内需不需要引用这个dll文件,无所谓,可写可不写。但是如果是其他dll,必须在CAPL程序中引用。为…

多服务器节点访问解决一人一单问题+redis设置锁方案

项目地址及项目具体介绍-码云仓库&#xff1a;https://gitee.com/flowers-bloom-is-the-sea/distributeNodeSolvePessimisticLockByRedis 测试1&#xff1a; 这里使用jmeter同时启动2各线程&#xff1a; 原来的数据库表的数据&#xff1a; goods的数据是&#xff1a; id …

金蝶云星空物料批量禁用反禁用程序

【需求描述】&#xff1a;需要通过批量禁用反禁用操作物料。 【操作方法】&#xff1a; 下载附件批量禁用程序。 首先双击安装&#xff0c;一直下一步。 双击打开 会提示需要配置账套信息 点击菜单栏配置 输入相关配置信息 填写完毕后 然后点击下载模板导入要下载的数据 选…

Linux进程学习【三】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Perseverance is not a long race; it is many short races one after another…

Word控件Spire.Doc 【Table】教程(18):如何在 C# 中的 Word 中创建嵌套表格

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

「JVM 编译优化」即时编译器

前端编译器&#xff08;javac&#xff09;将 Java 代码转为字节码&#xff08;抽象语法树&#xff09;&#xff0c;优化手段主要用于提升程序的编码效率&#xff1b; 后端编译器&#xff08;内置于 JVM 的 JIT/AOT Compiler&#xff0c;C1&#xff0c;C2&#xff09;将字节码转…

2022年休闲游戏市场总结

在预测 2023 年之前&#xff0c;我们先回顾一下 2022 年。从上一年发生的事件中往往能看到未来趋势的影子&#xff0c;所以 2022 年的总结至关重要。一、2022年总结回顾1、流行游戏类型回顾 2022 年&#xff0c;三种超休闲游戏表现最为突出&#xff1a;跑酷游戏&#xff1a;跑酷…