DAY19:二叉树(九)路径总和+已知中后序构造二叉树

news2025/2/10 8:19:36

文章目录

    • 112.路径总和
      • 思路
      • 伪代码
      • 完整版写法1
        • 写法1必须分开两个函数的原因
        • 注意点
      • 完整版写法2
        • 写法2不涉及到回溯的原因
    • 106.中序和后序遍历构造二叉树
      • 思路
      • 伪代码
        • 后序数组如何切割出左右区间
        • 写法注意
        • 区间切割注意
        • 中序和前序如何唯一构造二叉树
        • 后序和前序能否唯一构造二叉树?
          • 补充:
      • 完整版
        • 切割中序数组
          • vector数组创建
          • cppSTL定义区间默认的左闭右开规则
          • 关于左闭右开原则cpp适用性
        • 切割后序数组
        • 补全的完整版

112.路径总和

  • 本题因为一些奇怪的原因卡了很久,注意复盘

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
在这里插入图片描述
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

思路

这道题同样没有中的处理逻辑,我们要遍历从根节点到叶子节点的路径看看总和是不是目标和。

对于递归的做法,前中后序都可以,因为没有中节点逻辑,只是左右子树一直计算节点值之和。

递归首先确定参数与返回值,此处的递归参数除了根节点之外,仍然有其他的参数,也就是传入的总和的数值。

伪代码

  • 找到一条路径立刻返回,靠的就是返回值
  • 从0开始累加再判断和目标和是否相等,比较麻烦,可以考虑用递减,以初始的target作为目标值,每一层减去节点val,如果遍历到了叶子节点并且target减到0,就可以返回true
bool travelsal(TreeNode* root,int target){
    //传入的参数也需要传计数器,累计每一条路径上节点的和  
    //从0开始逐渐累加比较麻烦,可以选择直接传入总和result,每到一个节点做减法操作
    //终止条件:叶子节点
    if(root->left==NULL&&root->right==NULL&&target==0){
        return true;
    }
    
    //单层递归:target--
    if(root->left){
        //本题目递归函数有返回值,需要对返回值进行处理判断
        bool result= travelsal(root->left,target-root->left->val);
        if(result){
            return true;
        }
    }
    if(root->right){
        bool result = travelsal(root->left,target-root->left->val);
        if(result){
            return true;
        }
    }
    return false;    
}

完整版写法1

class Solution {
public:
    bool travelsal(TreeNode* root,int target){
    //传入的参数也需要传计数器,累计每一条路径上节点的和  
    //从0开始逐渐累加比较麻烦,可以选择直接传入总和result,每到一个节点做减法操作
    //终止条件:叶子节点
    if(root->left==NULL&&root->right==NULL&&target==0){
      return true;
    }
    //单层递归:target--
    if(root->left){
        //本题目递归函数有返回值,需要对返回值进行处理判断
        bool result= travelsal(root->left,target-root->left->val);
        if(result){
            return true;
        }
    }
    if(root->right){
        //要注意如果左右子树逻辑相同,复制代码过来的时候一定要把所有的left都改成right,不能漏掉……
        bool result = travelsal(root->right,target-root->right->val);
        if(result){
            return true;
        }
    }
    //如果上面的情况都不满足,就return false
    return false;    
}
   bool hasPathSum(TreeNode* root, int targetSum) {
       if(root==NULL){
           return false;
       }
       return travelsal(root,targetSum-root->val);
    
  }
};

写法1必须分开两个函数的原因

这种写法,需要把递归的部分单独拿出来,然后在主函数中传入travelsal(root,targetSum-root->val),也就是说,需要在传入的时候就对根节点本身就是叶子节点的情况进行处理

如果只写一个函数,那么,当二叉树只有一个叶子节点的时候,也就是输入[1],sum=1的时候,输出是相反的。因为只写一个函数的话,没有处理二叉树根节点的root->val

最开始只写一个函数的写法:

  • 如果想要只写一个函数,应当参考写法2,在最开始就进行targetSum-root->val,然后左右子树的递归中,不再对targetSum进行数值处理。将写法1改写为只需要一个函数,那么它将与写法2变得非常相似。
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
    //传入的参数也需要传计数器,累计每一条路径上节点的和
    if(root==NULL){
        return false;
    }
    //从0开始逐渐累加比较麻烦,可以选择直接传入总和result,每到一个节点做减法操作
    bool result;
    //终止条件:叶子节点
    if(root->left==NULL&&root->right==NULL){
        if(targetSum==0){
            return true;
        }
        return false;
    }
    //单层递归:target--
    if(root->left){
        targetSum=targetSum-root->left->val;
        result = hasPathSum(root->left,targetSum);
        if(result){
            return true;
        }
        else{
            return false;
        }
        targetSum+=root->left->val;
        
    }
    if(root->right){
        targetSum = targetSum-root->right->val;
        result = hasPathSum(root->right,targetSum);
        if(result){
            return true;
        }
        else{
            return false;
        }
        targetSum += root->right->val;
    }
    return false;
  }
};

在这里插入图片描述

注意点

另一个注意点是,当左右子树逻辑相同的时候,如果要把左子树的代码复制粘贴到右子树,一定要把所有的left都改成right……因为这个问题卡bug了很久最后才发现,除此之外还有递归能直接返回就直接返回,有时候定义变量定义太多会给自己找麻烦。

完整版写法2

  • 这道题也可以不采取回溯,直接采用前序遍历,中左右的方式,中的处理逻辑是targetSum自减,不涉及到回溯。更推荐这种写法,不需要考虑root->val传入的时候先自减的问题。
  • 在最后直接return false,是因为前面所有的逻辑都会直接return,如果前面都没有return,就直接return false
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        //bool result;
         //终止条件
        if(root==NULL){
           return false;
        }
       //中的处理逻辑
        targetSum -= root->val;
        // 遇到叶子节点,并且计数为0,返回true
        if (!root->left && !root->right && targetSum == 0) {
            return true;
        }

        // 递归调用左子树和右子树,递归有返回值,直接接收返回值并判断
        if (hasPathSum(root->left, targetSum)){
            return true;
        }
        if (hasPathSum(root->right, targetSum)){
            return true;
        }
        //如果所有的情况都没有return,就return false
        return false;
  }
};

写法2不涉及到回溯的原因

写法1里面,需要回溯的原因是在左子树内部的操作中,本层递归的targetSum发生了改变,那么在遍历右子树的时候,本层递归的targetSum必须恢复到遍历左子树之前的值,不然targetSum的值会出错。

但是,写法2中,直接在前序遍历处理逻辑中,对targetSum进行了操作,不涉及到左子树的内部操作,因此就不需要在遍历右子树的时候,恢复到遍历左子树之前的值。

  • 能直接返回就直接返回,不要定义新的变量给自己找麻烦,比如写法2如果定义bool result的话,写法就麻烦很多

106.中序和后序遍历构造二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
在这里插入图片描述
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例2
输入:inorder = [-1], postorder = [-1]
输出:[-1]

思路

本题的要求是从两个中序和后序遍历两个数组中构造一棵二叉树。

中序是左中右,后序是左右中,我们构造二叉树的时候首先要确定根节点的值,但中序是左中右,也不知道中节点在哪里,此时就需要结合后序。后序是左右中,也就是说,后序数组最后一个元素一定是根节点

例如示例1,根节点一定是3,因为是后序(左右中遍历)数组的最后一个。

但是仅看后序无法区分左右,需要结合中序。当根节点是3的时候,左子树就是9右子树就是15 20 7。能够直接确定左右子树里面所有的节点值。
在这里插入图片描述
也就是说,我们先根据后序找到中间的节点,再根据中序和中间节点,在中序数组中完成左右子树切割的任务。

得到根节点左右子树后,再看后序数组,来明确左右子树的中间节点。根据后序左右中的顺序,可以知道右子树的中间节点是20得到中间节点之后,再去切割中序,得到右子树中间节点的左右孩子是15和7。
在这里插入图片描述
大致思路可以概括为

  • 通过后序找到中间节点
  • 通过中序区分左右子树的大区间
  • 再根据大区间切割后序,在下一层递归的时候,再寻找左右子树大区间的中间节点。也就是找上一层节点的左右孩子

伪代码

文字版代码思路:

  • 后序数组为0,空节点
  • 后序数组不为0,找到后序数组最后一个元素为根节点
  • 寻找中序数组中根节点位置,切割得到根节点左右子树
  • 寻找后序中左右子树区间的位置,找到区间最后一个元素就是左右子树的中间节点
//确定递归参数和返回值,返回二叉树的根节点
TreeNode* travelsal(vector<int>inorder,vector<int>postorder){
    if(postorder.size()==0){
        return null;
    }
    //找到分割点
    int val = postorder[postorder.size()-1];
    TreeNode* root = new TreeNode(val);
    //后序数组=1就直接返回root
    if(postorder.size()==1){
        return root;
    }
    
    //单层递归逻辑
    int index;
    //寻找中序数组中根节点位置
    for(index=0;index<inorder.size();index++){
        if(inorder[index]==val){
            break;
        }
    }
    //break出来之后,此时的index就是根节点元素在中序数组的下标
    //切割中序数组,得到左中序数组和右中序数组
    
    //切割后序数组,切出左区间和右区间,并得到区间最后一个值就是左右孩子节点值
    //注意,切中序的时候已经知道了二叉树左区间,我们需要拿着中序数组左区间的大小去切后序数组
    
    //切割后序得到左后序数组和右后序数组,直接进行递归!
    root->left = travelsal(左中序数组,左后序数组);
    root->right = travelsal(右中序数组,右后序数组);
    return root;//返回二叉树
    
}

后序数组如何切割出左右区间

后序数组确定了根节点之后,可以根据中序数组中得到的,左右区间的长度,在后序数组中切割出左区间和右区间。切割中序用的是index,切割后序用的是中序数组左右区间长度信息。

写法注意

因为切割过程比较复杂,我们可以先写递归的逻辑,看递归需要什么,再去前面部 分补充切割操作。

区间切割注意

切中序数组和切后序数组的时候,一定要统一区间的规则,统一为左闭右开或者左开右闭。区间切割规则如果不统一很容易出现边界值问题。

切的时候需要先切割中序,根据后序得到的根节点信息先把中序切开,再拿着中序得到的左区间,去切出来后序里面的左区间

切区间的时候容易出问题,一定要打日志,要把中序数组和后序数组都打印出来,左中序/左后序也要打印出来。

中序和前序如何唯一构造二叉树

如果题目改成中序和前序,也能唯一构造出二叉树。中序是左中右,前序是中左右,我们可以根据前序数组先得到根节点(前序数组第一个),拿着中节点再去切中序数组。是同样的思路逻辑

后序和前序能否唯一构造二叉树?

不能。后序是左右中,前序是中左右,无法来区分左右区间,找不到分割点,就没办法进行切割。

中序把左右区间隔开的逻辑,才能知道左区间是什么,右区间是什么。

例如图上这种情况,就不能对此做出区分,但是这两棵二叉树是完全不同的二叉树
在这里插入图片描述

补充:

在上图结构的二叉树中

    1
   /
  2
 /
3

前序遍历的顺序是“根左右”,后序遍历的顺序是“左右根”。

  • 前序遍历:

    遵循“根左右”的顺序,先访问根节点1,然后访问左孩子2,再访问2的左孩子3,所以前序遍历的结果是 1-2-3。

  • 后序遍历:

    遵循“左右根”的顺序。由于这棵树没有右孩子,所以我们首先访问左孩子,即访问2,由于中间节点需要在最后访问,因此我们先不把2计入结果,继续访问2的左孩子3。在最后的结果中,对于每一棵子树,都是先写叶子节点,再写根节点,所以后序遍历的结果是 3-2-1。

后序访问方式的一个好处是,对于每一个节点,我们在访问该节点之前,已经访问了其所有子节点。这在一些算法问题中是非常有用的,比如计算树的高度,或者判断一棵树是否是另一棵树的子树等。

完整版

  • 第一步先把除了切割左中序/左后序数组的逻辑写完,主要是递归的逻辑
  • 本题是遍历,两个数组大小相同且是否为空也相同
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        //返回二叉树的根节点
        
        //如果数组大小是0,空节点
        if(inorder.size()==0&&postorder.size()==0){
            return NULL;
        }
        //找到根节点的元素值
        int val = postorder[postorder.size()-1];
        //给根节点赋值
        TreeNode* root = new TreeNode(val);
        //找中序数组中的根节点
        int index = 0;
        for(index;index<inorder.size();index++){
            if(inorder[index]==val){
                break; //跳出循环,相当于存储中序数组中val值的下标
            }
        }
        
        //根据得到的下标值,切割左中序数组
        
        //根据左中序数组的长度,切割左后序数组
        
        root->left = buidTree(左中序,左后序);
        root->right = buildTree(右中序,右后序);
        return root;
    }
};
  • 写好框架之后再来填写切割数组的逻辑
  • 切割的时候注意保持一致的切割原则!坚持循环不变量,这个原则在之前的螺旋数组里也很重要

切割中序数组

       //找中序数组中的根节点
        int index = 0;
        for(index;index<inorder.size();index++){
            if(inorder[index]==val){
                break; //跳出循环,相当于存储中序数组中val值的下标
            }
        }
        //根据得到的下标值,切割左中序数组,建立新的数组
		//左闭右开区间[0,index)
		vector<int>leftInorder(inorder.begin(),inorder.begin()+index);
		vector<int>rightInorder(inorder.begin()+index+1,inorder.end());
vector数组创建

构造函数:

// 1. 创建空vector; 常数复杂度
vector<int> v0;
// 1+. 这句代码可以使得向vector中插入前3个元素时,保证常数时间复杂度
v0.reserve(3);
// 2. 创建一个初始空间为3的vector,其元素的默认值是0; 线性复杂度
vector<int> v1(3);
// 3. 创建一个初始空间为3的vector,其元素的默认值是2; 线性复杂度
vector<int> v2(3, 2);
// 4. 创建一个初始空间为3的vector,其元素的默认值是1,
// 并且使用v2的空间配置器; 线性复杂度
vector<int> v3(3, 1, v2.get_allocator());
// 5. 创建一个v2的拷贝vector v4, 其内容元素和v2一样; 线性复杂度
vector<int> v4(v2);
// 6. 创建一个v4的拷贝vector v5,其内容是{v4[1], v4[2]}; 线性复杂度
vector<int> v5(v4.begin() + 1, v4.begin() + 3);
// 7. 移动v2到新创建的vector v6,不发生拷贝; 常数复杂度; 需要 C++11
vector<int> v6(std::move(v2));  // 或者 v6 = std::move(v2);

上面左中序数组的创建,使用的就是创建拷贝vector的方式

vector<int>leftInorder(inorder.begin(),inorder.begin()+index);
cppSTL定义区间默认的左闭右开规则

在C++中,当使用迭代器定义一个区间时,通常采用的是左闭右开的方式,即包含区间的起始位置,但不包含区间的结束位置。这一规则在大部分STL容器,包括vector,list,deque等的成员函数中都是通用的

例如:

vector<int> v5(v4.begin() + 1, v4.begin() + 3);

新创建的v5包含的元素是v4[1]v4[2]v4的下标从0开始,因此v4.begin() + 1指向的就是v4[1]),但是,并不包含v4.begin() + 3所指向的元素。也就是说,并不包含v4[3]

由于迭代器定义区间默认是左闭右开原则,因此,本题中切割数组最好也是左闭右开。例如,下面的代码构造新的左中序数组:

vector<int>leftInorder(inorder.begin(),inorder.begin()+index);

默认定义的就是左闭右开的区间,这句代码构造了一个inorder数组的拷贝数组,包含的元素从inorder[0]一直到inorder[index-1]。并不包含inorder[index]

关于左闭右开原则cpp适用性

在C++的**STL**(Standard Template Library,标准模板库)中,区间通常被定义为左闭右开,也就是说,区间包含开始位置但不包含结束位置。这个约定对于所有STL容器的成员函数都是适用的,例如vector、list、deque等的begin()、end()、rbegin()、rend()等函数。

对于非STL的一般编程情况,是否使用左闭右开的规则取决于程序员自己。左闭右开的优势在于它能够简化编程,因为在这种情况下,一个区间的结束位置正好是下一个区间的开始位置。

切割后序数组

		//根据切割得到的左中序和右中序数组的长度,对后序数组进行切割
		//左中序长度=左后序,右中序长度=右后序
		
		//后序数组先删除末尾元素,因为是根节点
		postorder.pop_back();
		//切割出左后序
		vector<int>leftPostorder(postorder.begin(),postorder.begin()+leftInorder.size());
		//切割出右后序
		vector<int>rightPostorder(postorder.begin()+leftInorder.size(),postorder.end());

补全的完整版

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        //返回二叉树的根节点
        
        //如果数组大小是0,空节点
        if(inorder.size()==0&&postorder.size()==0){
            return NULL;
        }
        //找到根节点的元素值
        int val = postorder[postorder.size()-1];
        //给根节点赋值
        TreeNode* root = new TreeNode(val);
        //找中序数组中的根节点,也就是分割点
        int index;
        for(index=0;index<inorder.size();index++){
            if(inorder[index]==val){
                break; //跳出循环,相当于存储中序数组中val值的下标
            }
        }
        
        //根据得到的下标值,切割左右中序数组
		//左闭右开区间[0,index)
		vector<int>leftInorder(inorder.begin(),inorder.begin()+index);
		vector<int>rightInorder(inorder.begin()+index+1,inorder.end());
        
        //根据左中序数组的长度,切割左右后序数组
		//后序数组先删除末尾元素,因为是根节点
		postorder.pop_back();
		//切割出左后序
		vector<int>leftPostorder(postorder.begin(),postorder.begin()+leftInorder.size());
		//切割出右后序
		vector<int>rightPostorder(postorder.begin()+leftInorder.size(),postorder.end());
        
        //递归
        root->left = buildTree(leftInorder,leftPostorder);
        root->right = buildTree(rightInorder,rightPostorder);
        return root;

    }
};

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

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

相关文章

03-踏入程序诗意:Golang 流程控制的优雅律动

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Golang基础 &#x1f4ac;Go&#xff08;又称Golang&#xff09;是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性&#xff0c;拥有高效的并发编程能力和简洁的语法。G…

Linux 安装Docker完整教程(六)

文章目录 背景一、Docker简介二、docker desktop 和 docker engin 区别三、Linux 安装Docker1. 安装docker的前置条件&#xff1a;2. 查看Docker版本3. 检查是否安装过Docker4. Docker的自动化安装 (不想自带化安装的可跳过本步骤&#xff0c;选择手动安装)5. Docker手动安装&a…

第二章CompletableFuture

文章目录 Future和Callable接口FutureTask实现类为什么引出FutureTask Future到CompletableFutureFuture优点Future的缺点get()阻塞isDone()轮询Future应用现状 CompletableFuture基本介绍CompletionStage核心的四个静态方法&#xff08;分为两组&#xff09;runAsync无返回值s…

数字人解决方案——实时对话数字人源码与环境配置

前言 1.从技术角度来看&#xff0c;现在的数学人就是一个缝合怪&#xff0c;把各种技术点都整合在一起&#xff0c;用来实现直播、对话等数字人。技术流程大概如下图&#xff1a; 其实最重要的一环应该属于LLM(大型语言模型)&#xff0c;LLM相当于一个人的意识&#xff0c;如果…

外卖订单管理系统(Javaweb+Mysql)

程序源码 可以通过上方代码包.rar文件下载&#xff0c;也可以在下方链接下载 链接: https://pan.baidu.com/s/1OruBEcEK70DtUbvA8UIE-w?pwddkdg &#xff08;数据库sql文件在项目根目录下data -> sql&#xff09; 设计报告 【金山文档】 外卖订单管理系统设计报告 http…

编译原理期末速成–正规式、NFA转DFA、DFA的简化

编译原理期末速成–正规式、NFA转DFA、DFA的简化 文章目录 编译原理期末速成--正规式、NFA转DFA、DFA的简化什么是DFA、NFA&#xff1f;看个题消化一下步骤一&#xff1a;步骤二&#xff1a;步骤三&#xff1a;步骤四&#xff1a;步骤五&#xff1a;步骤六&#xff1a;步骤七&a…

POJ The Game

原题目&#xff1a;传送锚点 1.题目 The Game Description A game of Renju is played on a 19*19 board by two players. One player uses black stones and the other uses white stones. The game begins in an empty board and two players alternate in placing black …

面对工作中的失误:从错误中学习与成长

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想…

C99的一些新特性记录

固长类型头文件<stdint.h> 由于历史原因&#xff0c;C语言中实现的整型数只保证了在不同硬件体系中的最小长度&#xff0c;因此在使用时&#xff0c;需要根据代码实际运行的平台来确定类型的长度&#xff0c;这导致代码非常不方便移植。C99标准通过增加固长类型头文件引入…

【Unity3D】屏幕深度和法线纹理简介

1 前言 1&#xff09;深度纹理和法线纹理的含义 深度纹理本质是一张图片&#xff0c;图片中每个像素反应了屏幕中该像素位置对应的顶点 z 值相反数&#xff08;观察坐标系&#xff09;&#xff0c;之所以用 “反应了” 而不是 “等于”&#xff08;或 “对应” &#xff09;&am…

chatgpt赋能python:Python浮点型转换为整型的方法和应用场景

Python浮点型转换为整型的方法和应用场景 介绍 Python的浮点型和整型在数值计算中应用广泛。有时候我们需要将一个浮点数转换为整数&#xff0c;这时候就需要使用Python提供的一些函数来完成转换。本文将介绍Python浮点型转换为整型的方法和应用场景。 浮点型和整型的区别 …

初探MyBatis实现简单查询

一、创建数据库与表 1、创建数据库 在Navicat里创建MySQL数据库 - testdb&#xff0c;采用utf8mb4字符集 2、创建用户表 CREATE TABLE t_user (id int(11) NOT NULL AUTO_INCREMENT,name varchar(50) DEFAULT NULL,age int(11) DEFAULT NULL,address varchar(255) DEFAULT…

SpringBoot的日志文件

文章目录 前言日志怎么用自定义打印日志⽇志级别 - 了解⽇志持久化Lombok提供的方法 前言 上文讲述了 SpringBoot项目的构建 与配置文件的使用 ,下面来介绍 SpringBoot 的日志文件 , 日志在程序 中起到的作用是很大的 , 谁写的程序能不报错误呢, 日志就是一种让你快速找到错误…

Linux环境变量配合权限维持手法

前言&#xff1a; 权限维持的时候有其中有两种&#xff0c;一种是alias别名、第二种是prompt_command&#xff0c;这里我们可以将其添加到环境变量中&#xff0c;每次运行的时候都可以使用&#xff0c;从而达到权限控制的效果&#xff0c;而不是临时执行的效果。 环境变量&am…

harbor仓库的搭建

harbor仓库的搭建 前言一、准备二、registry私有仓库拉取registry镜像上传镜像下载镜像添加私有仓库解析配置使用非加密端口拉取镜像 三、仓库加密域名保持一致部署客户端证书&#xff0c;不然会报错验证仓库认证删除registry&#xff0c;重建登录仓库&#xff0c;不然无法上传…

[论文阅读笔记76]GPT Understands, Too(P-tuning)

1. 基本信息 题目论文作者与单位来源年份GPT Understands, Too清华大学 Citations, References 论文链接&#xff1a;https://arxiv.org/pdf/2103.10385.pdf 论文代码&#xff1a; 2. 要点 研究主题问题背景核心方法流程亮点数据集结论论文类型关键字微调大模型采用传统微…

css空间转换

目录 1. 3D移动 translate3d 1.1 三维坐标系 1.2 3D移动 translate3d 1.3 透视 perspective 1.4 translateZ 2. 3D旋转 rotate3d 2.1 左手法则-判断元素旋转方向的取值正负 3. 3D呈现 transform-style【***】 4. 3D缩放 transform:scale3d 1. 3D移动 translate3d …

nacos运行报错-jar: file does not existCan‘t retrieve image ID from build stream

一、问题 Deploying nacos Dockerfile: ruoyi-visual/ruoyi-nacos/Dockerfile… Building image… Preparing build context archive… [>]211/211 files DoneSending build context to Docker daemon… [>] 6.099MB DoneStep 1/8 : FROM openjdk:11---> 5505a9a39df…

chatgpt赋能python:用Python创建股票池

用Python创建股票池 介绍 如果你是一位投资者&#xff0c;你一定知道股票池是什么。它是一个包含一组股票的集合&#xff0c;使投资者能够跟踪和管理他们的投资组合。这些股票可以根据各种因素分类&#xff0c;例如行业&#xff0c;市值&#xff0c;收入增长等。 Python是一…

Oracle的学习心得和知识总结(二十六)|Oracle数据库Real Application Testing测试指南(数据库回放)

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…