数据结构(二叉树)

news2024/11/18 1:27:16

文章目录

  • 一、树的基础概念
    • 1.1 树型结构
    • 1.2 树型的概念
  • 二、二叉树
    • 2.1 概念 + 性质
    • 2.2 二叉树的存储
    • 2.2 二叉树的基本操作
      • (1)遍历
      • (2)其他
    • 2.3 二叉树练习

一、树的基础概念

1.1 树型结构

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 有一个特殊的结点,称为根结点,根结点没有前驱结点
  • 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集合Ti (1 <= i <=m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  • 树是递归定义的

在这里插入图片描述

1.2 树型的概念

结点的度:一个结点含有子树的个数称为该结点的度
树的度:一棵树中,所有结点度的最大值称为树的度
叶子结点或终端结点:度为0的结点称为叶子结点; 如上图:B、C、H、I…等节点为叶结点
双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点
根结点:一棵树中,没有双亲结点的结点
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
树的高度或深度:树中结点的最大层次

-------了解-------------------------
非终端结点或分支结点:度不为0的结点
兄弟结点:具有相同父结点的结点互称为兄弟结点
堂兄弟结点:双亲在同一层的结点互为堂兄弟
结点的祖先:从根到该结点所经分支上的所有结点
子孙:以某结点为根的子树中任一结点都称为该结点的子孙
森林:由m(m>=0)棵互不相交的树组成的集合称为森林

二、二叉树

2.1 概念 + 性质

❤️关于类别的概念

一棵二叉树是结点的一个有限集合,该集合:
在这里插入图片描述

❤️两种特殊的二叉树

在这里插入图片描述

❤️二叉树的性质
在这里插入图片描述
在这里插入图片描述

2.2 二叉树的存储

  • 顺序存储
  • 类似于链表的链式存储:通过一个一个的节点引用起来的,常见的表示方式有孩子表示法和孩子双亲表示法
// 孩子表示法
class Node {
	int val; // 数据域
	Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
// 孩子双亲表示法
class Node {
	int val; // 数据域
	Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
	Node parent; // 当前节点的根节点
}

2.2 二叉树的基本操作

(1)遍历

  • 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点—>根的左子树—>根的右子树
// 前序遍历
     //无返回值
    public void preOrder(TreeNode root) {
        if(root == null) return;
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }

 
    //遍历思路,要求返回的是List<Integer>
    List<Integer> ret = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root == null) return ret;
        //System.out.print(root.val+" ");
        ret.add(root.val);
        preorderTraversal(root.left);    //实际上没用到返回值
        preorderTraversal(root.right);
        return ret;
    }
 
    //子问题
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root == null) return ret;
        ret.add(root.val);       //用上了返回值
        List<Integer> leftTree = preorderTraversal(root.left);
        ret.addAll(leftTree);
        List<Integer> rightTree = preorderTraversal(root.right);
        ret.addAll(rightTree);
        return ret;
    }
  • 中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树
// 中序遍历
    public void inOrder(TreeNode root) {
        if(root == null) return;
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }
  • 后序遍历(Postorder Traversal)——根的左子树—>根的右子树—>根节点
    // 后序遍历
    public void postOrder(TreeNode root) {
        if(root == null) return;
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val+" ");
    }
  • 层序遍历
    • 设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历

不能根据前序遍历和后续遍历创建一个二叉树,因为前序和后续确定的都是根,确定不了左和右

(2)其他

1.结构

public class BinaryTree {
    static class TreeNode {
        public char val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(char val) {
            this.val = val;
        }
    }
}

2.获取树中节点的个数

 public static int usedSize = 0;
 // 获取树中节点的个数
 public int size(TreeNode root) {
     if(root == null) {
         return 0;
     }
     usedSize++;
     size(root.left);
     size(root.right);
     return usedSize;
 }

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

3.获取叶子节点的个数

 public static int leafSize = 0;
 public int getLeafNodeCount(TreeNode root) {
     if(root == null) {
         return 0;
     }
     if(root.left == null && root.right == null) {
         leafSize++;
     }
     getLeafNodeCount(root.left);
     getLeafNodeCount(root.right);
     return leafSize;
 }

 public int getLeafNodeCount2(TreeNode root) {
     if(root == null) {
         return 0;
     }
     if(root.left == null && root.right == null) {
         return 1;
     }
     return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
 }

4.获取第K层节点的个数

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

5.获取二叉树的高度

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

     return (leftH > rightH ? leftH :rightH) + 1;
 }

 public  int getHeight2(TreeNode root) {
     if(root == null) {
         return 0;
     }

     return (getHeight2(root.left) > getHeight2(root.right) ?
             getHeight2(root.left) :getHeight2(root.right)) + 1;
 }

6.检测值为value的元素是否存在

 public TreeNode find(TreeNode root,int val) {
     if(root == null) return null;
     if(root.val == val) {
         return root;
     }
     TreeNode leftL = find(root.left,val);
     if(leftL != null) {
         return leftL;
     }
     TreeNode leftLR = find(root.right,val);
     if(leftLR != null) {
         return leftLR;
     }
     return null;
 }

8.层序遍历

public void levelOrder(TreeNode root) {
    Queue<TreeNode> queue = new LinkedList<>();
    if(root != null) {
        queue.offer(root);
    }
    while (!queue.isEmpty()) {
        TreeNode top = queue.poll();
        System.out.print(top.val+" ");
        if(top.left != null) {
            queue.offer(top.left);
        }
        if(top.right != null) {
            queue.offer(top.right);
        }
    }

}

public List<List<Integer>> levelOrder2(TreeNode root) {
    List<List<Integer>> ret = new ArrayList<>();
    if(root == null) {
        return ret;
    }
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        int size = queue.size();//这一层节点的个数
        List<Integer> list = new ArrayList<>();
        while (size != 0) {
            TreeNode top = queue.poll();
            //System.out.print(top.val+" ");
            list.add(top.val);
            if(top.left != null) {
                queue.offer(top.left);
            }
            if(top.right != null) {
                queue.offer(top.right);
            }
            size--;
        }
        ret.add(list);
    }
    return ret;
}

9.判断一棵树是不是完全二叉树

 public boolean isCompleteTree(TreeNode root) {
     Queue<TreeNode> queue = new LinkedList<>();
     if(root != null) {
         queue.offer(root);
     }
     while (!queue.isEmpty()) {
         TreeNode cur = queue.poll();
         if(cur != null) {
             queue.offer(cur.left);
             queue.offer(cur.right);
         }else {
             break;
         }
     }
     while (!queue.isEmpty()) {
         TreeNode cur = queue.poll();
         if(cur != null) {
             return false;
         }
     }
     return true;
 }

10.在root这棵树当中 找到node这个节点上的位置

public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
    if(root == null) {
        return false;
    }
    stack.push(root);
    if(root == node) {
        return true;
    }
    boolean ret = getPath(root.left,node,stack);
    if(ret == true) {
        return true;
    }

    boolean ret2 = getPath(root.right,node,stack);
    if(ret2 == true) {
        return true;
    }
    stack.pop();
    return false;
}

11.求最大深度

 public int maxDepth(TreeNode root) {
     if(root == null) {
         return 0;
     }
     int leftH = maxDepth(root.left);
     int rightH = maxDepth(root.right);

     if(leftH >= 0 && rightH >= 0 &&
             Math.abs(leftH-rightH) <= 1) {
         return Math.max(leftH,rightH) + 1;
     }else {
         return -1;
     }
 }

2.3 二叉树练习

1.检查两棵树是否相同

 public boolean isSameTree(TreeNode p, TreeNode q) {
     if(p == null && q != null || p != null && q == null) {
         return false;
     }
     if(p == null && q == null) {
         return true;
     }
     //一定是p 和 q 都不等于空!
     if(p.val != q.val) {
         return false;
     }
     return isSameTree(p.left,q.left)
             && isSameTree(p.right,q.right);
 }

2.另一棵数的子树

 // 时间复杂度: O(min(m,n))
 public boolean isSameTree(TreeNode p, TreeNode q) {
     if(p == null && q != null || p != null && q == null) {
         return false;
     }
     if(p == null && q == null) {
         return true;
     }
     //一定是p 和 q 都不等于空!
     if(p.val != q.val) {
         return false;
     }
     return isSameTree(p.left,q.left)
             && isSameTree(p.right,q.right);
 }

 //时间复杂度:O(S*T)
 //每个s  都要和 t 判断是不是相同的!
 public boolean isSubtree(TreeNode root, TreeNode subRoot) {
     if(root == null) {
         return false;
     }
     if(isSameTree(root,subRoot)) {
         return true;
     }
     if(isSubtree(root.left,subRoot)) {
         return true;
     }
     if(isSubtree(root.right,subRoot)) {
         return true;
     }
     return false;
 }

3.翻转二叉树

 public TreeNode invertTree(TreeNode root) {
     if(root == null) return null;

     TreeNode tmp = root.left;
     root.left = root.right;
     root.right = tmp;

     invertTree(root.left);
     invertTree(root.right);
     return root;
 }

4.是否是平衡二叉树

 //时间复杂度:
 public boolean isBalanced(TreeNode root) {
     if(root == null) return true;

     int leftHight = getHeight(root.left);
     int rightHight = getHeight(root.right);

     return Math.abs(leftHight-rightHight) < 2
             && isBalanced(root.left)
             && isBalanced(root.right);
 }

 //O(n)
 public boolean isBalanced1(TreeNode root) {
     if(root == null) return true;
     return maxDepth(root) >= 0;
 }

5.对称二叉树

 public boolean isSymmetric(TreeNode root) {
     if(root == null) return true;
     return isSymmetricChild(root.left,root.right);
 }
 public boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {
     if(leftTree == null && rightTree != null ||
             leftTree != null && rightTree == null ) {
         return false;
     }
     if(leftTree == null && rightTree == null) {
         return true;
     }
     if(leftTree.val != rightTree.val) {
         return false;
     }
     return isSymmetricChild(leftTree.left,rightTree.right)
             && isSymmetricChild(leftTree.right,rightTree.left);
 }

6.二叉树前序非递归遍历实现

 public void preOrderNor(TreeNode root) {
     if(root == null) return;
     Stack<TreeNode> stack = new Stack<>();
     TreeNode cur = root;

     while (cur != null || !stack.empty()) {
         while (cur != null) {
             stack.push(cur);
             System.out.print(cur.val + " ");
             cur = cur.left;
         }
         //cur == null
         TreeNode top = stack.pop();
         cur = top.right;
     }
 }

7.二叉树中序非递归遍历实现

 public void inOrderNor(TreeNode root) {
     if(root == null) return;
     Stack<TreeNode> stack = new Stack<>();
     TreeNode cur = root;

     while (cur != null || !stack.empty()) {
         while (cur != null) {
             stack.push(cur);
             cur = cur.left;
         }
         //cur == null
         TreeNode top = stack.pop();
         System.out.print(top.val + " ");
         cur = top.right;
     }
 }

8.二叉树后序非递归遍历实现

 public void postOrderNor(TreeNode root) {
     if(root == null) return;
     Stack<TreeNode> stack = new Stack<>();
     TreeNode cur = root;
     TreeNode prev= null;
     while (cur != null || !stack.empty()) {
         while (cur != null) {
             stack.push(cur);
             cur = cur.left;
         }
         //cur == null
         TreeNode top = stack.peek();

         if(top.right == null || top.right == prev) {
             System.out.print(top.val+" ");
             prev = top;//记录下来当前的top已经被打印过了
             stack.pop();
         }else {
             cur = top.right;
         }
     }
 }

9.二叉树的构建和遍历

import java.util.Scanner;
class TreeNode {
    public char val;
    public TreeNode left;
    public TreeNode right;

    public TreeNode(char val) {
        this.val = val;
    }
}

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String str = in.nextLine();
            TreeNode root = createTree(str);
            inOrder(root);
        }
    }

    public static int i = 0;
    public static TreeNode createTree(String str) {

        TreeNode root = null;
        
        if(str.charAt(i) != '#') {

            root = new TreeNode(str.charAt(i));
            i++;

            root.left = createTree(str);
            root.right = createTree(str);

        }else {
            i++;
        }


        return root;
    }

    public static void inOrder(TreeNode root) {
        if (root == null) return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
}

10.二叉树的最近公共祖先

 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
     if(root == null) return null;

     if(p == root || q == root) {
         return root;
     }

     TreeNode leftRet = lowestCommonAncestor(root.left,p,q);
     TreeNode rightRet = lowestCommonAncestor(root.right,p,q);

     if(leftRet != null && rightRet != null) {
         return root;
     }else if(leftRet != null) {
         return leftRet;
     }else {
         return rightRet;
     }
 }



 public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
     if(root == null) return null;

     Stack<TreeNode> s1 = new Stack<>();
     getPath(root,p,s1);

     Stack<TreeNode> s2 = new Stack<>();
     getPath(root,q,s2);

     int size1 = s1.size();
     int size2 = s2.size();

     if(size1 > size2) {
         int size = size1 - size2;
         while (size != 0) {
             s1.pop();
             size--;
         }
     }else {
         int size = size2 - size1;
         while (size != 0) {
             s2.pop();
             size--;
         }
     }
     //两个栈当中 的元素 是一样大小的
     while (!s1.empty() && !s2.empty()) {
         TreeNode tmp1 = s1.pop();
         TreeNode tmp2 = s2.pop();
         if(tmp1 == tmp2) {
             return tmp1;
         }
     }
     return null;
 }

11.根据二叉树创建字符串

class Solution {
    public String tree2str(TreeNode root) {
        if (root == null) return null;

        StringBuilder StringBuilder = new StringBuilder();

        tree2strChild(root, StringBuilder);

        return StringBuilder.toString();
    }

    private void tree2strChild(TreeNode t, StringBuilder stringBuilder){
        if (t == null) return;

        stringBuilder.append(t.val);

        if (t.left != null){
            stringBuilder.append("(");
            tree2strChild(t.left, stringBuilder);
            stringBuilder.append(")");
        }else{
            if (t.right == null){
                return;
            }else{
                stringBuilder.append("()");
            }
        }

        if (t.right != null){
            stringBuilder.append("(");
            tree2strChild(t.right, stringBuilder);
            stringBuilder.append(")");
        }else{
            return;
        }
    }
}

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

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

相关文章

ChatGPT 由0到1接入 Siri

ChatGPT 由0到1接入 Siri ChatGPT 由0到1接入 Siri第一步:获取 OpenAPI 的 Key第二步:制作快捷指令本教程收集于: AIGC从入门到精通教程 ChatGPT 由0到1接入 Siri 分享如何将 GPT 应用集成到苹果手机的 Siri 中 (当然手机是需要魔法(TZ)的) 第一步:获取 OpenAPI 的…

网络安全可以从事哪些岗位?岗位职责是什么?

伴随着社会的发展&#xff0c;网络安全被列为国家安全战略的一部分&#xff0c;因此越来越多的行业开始迫切需要网安人员&#xff0c;也有不少人转行学习网络安全。那么网络安全可以从事哪些岗位?岗位职责是什么?相信很多人都不太了解&#xff0c;我们一起来看看吧。 1、安全…

电阻阻值读取方法、电容容值的读取方法

电阻、电容的数值读取方法 文章目录 电阻、电容的数值读取方法前言1、电阻读数1.1 贴片电阻1.2.直插色环电阻 2、电容读数2.1 电容单位换算2.2 电容读数方法 前言 现在随着电子产品的不断升级优化&#xff0c;做到体积越来越小了&#xff0c;以前发现还是用得很多直插电阻和一…

百年不用了,今天拾起来 sort() 排序

简单赘述一下需求。 原本前端调用后端接口是自带排序功能的&#xff0c;一般是按照创建单据的时间&#xff0c;后端会处理好返回给我们。 但是有时候有特别的限制&#xff0c;需要前端自行处理排序展示。 如上图所示&#xff0c; 列表和列表扩展行均要根据我们新增或编辑的时候…

ATTCK v13版本战术介绍——防御规避(五)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权战术理论知识及实战研究、部分防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中防御规避战术第25-30种子技术&#xff0c;后续会介绍防御规避其他子技术&#xf…

什么蓝牙耳机好?业内权威蓝牙耳机排名TOP5

蓝牙耳机是当下最热门的数码产品&#xff0c;我个人已经买过十来款蓝牙耳机了&#xff0c;最近逛论坛看到知名数码论坛公布了蓝牙耳机排名TOP5&#xff0c;不懂什么蓝牙耳机好的朋友们在选购时可以从中入围的品牌中进行挑选。 一、JEET Air2蓝牙耳机 推荐理由&#xff1a;舒适…

springboot项目部署教程【本地+云服务器】

目录 前言一、环境准备二、项目导入三、配置Maven四、数据库导入五、启动项目六、浏览器访问结语 前言 springboot项目部署教程用最简单、暴力的方法完成项目导入。 &#x1f947;个人主页&#xff1a;MIKE笔记 &#x1f948;文章专栏&#xff1a;毕业设计源码合集 ⛄联系博主…

一、LLC 谐振变换器工作原理分析

1 前言 LLC 谐振电路采用脉冲频率调制(PFM)&#xff0c;通过改变驱动信号的频率来控制变换器的能量传输。谐振电路中的三个谐振元件为&#xff1a;谐振电感 Lr、谐振电容 Cr 和励磁电感 Lm&#xff0c;它们根据工作模式的不同可形成两个谐振频率。与串联谐振变换器相比&#x…

vue 组件 隐藏内容,点击展示更多功能

效果图 代码 <template><div class"m-text-overflow modules"><div class"l-content" :style"contentStyle"><div ref"refContent"><slot><span v-html"content"> </span></…

基于AT89C52单片机的万年历设计与仿真

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/87777668?spm1001.2014.3001.5503 源码获取 主要内容&#xff1a; 本次设计所提出的一种基于单片机技术的万年历的方案&#xff0c;能更好的解决万年历显示的问题…

公司招人,面试了一个4年经验要20K的,一问自动化都不会····

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。 看简历很多都是3、4年工作经验&#xf…

【腾讯云 Finops Crane集训营】Finops Crane究竟能为我们带来什么价值和思考?深入探究Crane

目录 前言 一、Crane目的是什么&#xff1f; 二、Crane有哪些功能&#xff1f; 1.成本可视化和优化评估 2.推荐框架 3.基于预测的水平弹性器 4.负载感知的调度器 5.拓扑感知的调度器 6.基于 QOS 的混部 三.Crane的整体架构及特性 1.Crane架构 Craned Fadvisor Metr…

postman runner使用外部数据

场景: 使用postman进行接口测试&#xff0c;需要对一个collection中的所有接口进行测试&#xff0c;或者需要使用指定的参数对collection中的接口进行测试。 工具&#xff1a; Postman for Windows Version 10.12.0接口文件&#xff08;链接&#xff1a;https://pan.baidu.co…

环境土壤物理模型HYDRUS建模方法

查看原文>>>系统学习环境土壤物理模型HYDRUS建模方法与多案例应用 目录 一、HYDRUS模型概述 二、土壤和地下水流问题基础知识 三、 溶质运移问题模拟 四、热量传输问题模拟 五、模型外部接口 其它生态环境相关推荐 HYDRUS是由著名土壤学家Rien van Genuchten和…

物联网| 定时器计数器开发之中断方法|定时器中断处理函数|完整测试代码|物联网之蓝牙4.0 BLE基础-学习笔记(6)

文章目录 11 定时器计数器开发之中断方法定时器中断处理函数:完整测试代码&#xff1a; 11 定时器计数器开发之中断方法 LED控制电路同前节&#xff1a; CC2530的T3定时器(8位&#xff09;需要了解T3GJL,T3CCTLO,T3CCO,T3CCTL1,T3CC寄存器。如下表所示&#xff1a; 按照表格…

母亲节海外网红营销指南:在2023年打造品牌曝光和销售增长

随着全球电商的迅速发展和社交媒体的普及&#xff0c;海外网红营销已成为出海品牌的重要策略之一。母亲节这样一个特殊的节日&#xff0c;对于出海品牌来说&#xff0c;是与消费者建立深层次情感联系的理想时机。本文Nox聚星将和大家详细探讨2023年出海品牌如何在母亲节期间做好…

基于AI技术的API开发工具,自动化和智能化快速高效开发API

一、开源项目简介 ApiCat 是一款基于 AI 技术的 API 开发工具&#xff0c;它旨在通过自动化和智能化的方式&#xff0c;帮助开发人员更快速、更高效地开发 API。ApiCat 支持 OpenAPI 和 Swagger 的数据文件导入和导出&#xff0c;并可以对用户输入的 API 需求进行分析和识别&a…

React hooks源码阅读

一、版本 react&#xff1a;17.0.2react-dom&#xff1a; 17.0.2 二、代码仓库 react源码的管理方式是monorepo模式&#xff0c;它把react中相对独立的模块分割出来作为一个软件包&#xff08;例如&#xff1a;react包、react-dom包、react-server包等等&#xff09;&#x…

SpringBoot整合logback日志

一、概述 与log4j相比&#xff1a; 实际上&#xff0c;这两个日志框架都出自同一个开发者之手&#xff0c;Logback 相对于 Log4J 有更多的优点 (1)logback不仅性能提升了&#xff0c;初始化内存加载也更小了。 (2)内容更丰富的文档 (3&#xff09;更强大的过滤器 二、步骤…

公网远程ERP - 在外远程登录公司局域网金蝶云ERP管理系统

文章目录 前言1.金蝶安装简介2. 安装cpolar内网穿透3. 创建安全隧道映射4. 在外远程访问金蝶云星空管理中心5. 固定访问地址6. 配置固定公网访问地址7.创建数据中心简介8.远程访问数据中心9. 固定远程访问数据中心地址10. 配置固定公网访问地址 前言 金蝶云星空聚焦多组织&…