DAY29:回溯算法(四)组合总和+组合总和Ⅱ

news2024/10/5 16:21:47

文章目录

    • 39.组合总和
      • 思路
      • 伪代码
        • 为什么传入i而不是i+1,不会导致无限循环
      • 完整版
      • 剪枝优化
      • 剪枝修改完整版
        • 补充:std::sort升降序的问题(默认升序)
    • 40.组合总和Ⅱ
      • 思路
      • 最开始的写法
      • debug测试:逻辑错误
      • 修改完整版:去重
        • used数组的作用,为什么不能删掉
      • debug测试:
        • 1. Char 9: runtime error: reference binding to null pointer of type 'int' (stl_vector.h)
        • 2. Line 1034: Char 34: runtime error: addition of unsigned offset to 0x603000000070 overflowed to 0x60300000006c (stl_vector.h)
      • 总结
        • vector的动态特性补充

39.组合总和

  • 因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
23 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

思路

本题没有重复元素,所以暂且不涉及去重问题。

与组合的一个很大区别就是,本题没有涉及到k,也就是没有规定一定要K个元素总和为target。而本题没有限制K,是靠目标和sum的值来控制树的深度

另外,题目中说candidates 中的 同一个 数字可以 无限制重复被选取 ,这也就是说,组合里可以存在重复的元素!也就是说绘制树形结构的时候,剩下的元素不能去掉当前元素本身。树形图示例如下:

在这里插入图片描述
这里有一个问题,因为元素本身被选取的次数是不限制的一共选取的元素个数也是不限制的,那么应该如何限制本身被选了多少次呢?因为自身如果一直被选择下去,就会死循环了,唯一的限制就是和targetSum这个值。

因为1 <= candidates[i] <= 200,所以说并不存在出现0的情况,也就是说我们可以根据这条分支的sum值大于target数值,直接进行return遍历下一个分支!也就是下图中粉色线条删除的部分,后面一定是在这个分支上继续累加,因为没有0和负数,所以sum只会越来越大

在这里插入图片描述

伪代码

  • 组合问题基本都是定义一个一维组合数组,一个二维结果数组
  • 因为本题是组合问题而不是子集问题,本题是从一个集合里面搜索符合条件的所有组合,因此需要startIndex来控制搜索的起点!
  • 如果已经大于了,就没有必要再往组合里面添加元素了!应该直接return,return了就可以进行元素修改了
  • 元素本身在组合里可重复选择,区别就在于startIndex这里,下层依然是i开始,而不是i+1。注意startIndex只是个参数,参数是根据i来改变的!
void backtracking(vector<vector<int>>&result,vector<int>&path,vector<int>&candidates,int target,int sum,int startIndex){
    //终止条件,这里不能只写if(sum==target),因为本题没有K的限制,如果一直没有,会一直搜索下去!
    //如果大于,后面就没必要再添加元素了,应该进行修改元素
    if(sum>target){
        return;
    }
    if(sum==target){
        result.push_back(path);
        return;
    }
    //单层搜索
    for(int i=startIndex;i<candidate.size();i++){
        sum += candidates[i];
        path.push_back(candidates[i]);
        //本身可重复,区别就在递归传入startIndex参数这里!这里下层依然是i开始,而不是i+1!
        backtracking(result,path,candidates,target,sum,i);
        //回溯
        sum -= candidates[i];
        path.pop_back();
    }
    
}

为什么传入i而不是i+1,不会导致无限循环

因为递归的时候,我们传入的循环开始参数是i而不是i+1,因此会考虑到递归能不能进行下去,会不会导致无限循环的问题。

backtracking(result,path,candidates,target,sum,i);

当我们传递i作为startIndex,我们允许下一次递归从相同的位置开始,从而包含当前正在处理的元素。因此,如果当前元素可以被多次选择以达到目标值,那么这种情况也会被考虑到。如果我们传递i+1作为startIndex那么下一次递归将会跳过当前元素,不再考虑重复选择当前元素的情况。

然而,我们需要注意的是,这个递归算法并不会陷入无限循环,因为我们有两个明确的终止条件:当我们的总和超过目标值或者等于目标值时,我们将停止递归。例如下图的情况:
在这里插入图片描述

另外,另一个误区是,尽管我们在递归调用中传递了i(这意味着我们可能会多次选择同一个元素),但这并不会阻止递归进行下去。因为在for循环中,for循环会在pop本层不符合条件的i之后,继续尝试i+1的选项

在这个回溯算法中,我们是在**for循环中遍历candidates数组的每一个元素。当我们在一次递归调用中传递了i而不是i+1,我们实际上仅仅是在允许算法选择当前的元素candidates[i]多次**。然而,这并不会阻止大的for循环在后续的迭代中尝试其他的元素。

完整版

class Solution {
public:
    void backtracking(vector<int>&path,vector<vector<int>>&result,vector<int>& candidates, int target,int sum,int startIndex){
        //终止条件
        if(sum>target){
            return;
        }
        if(sum==target){
            result.push_back(path);
            return;
        }
        //单层搜索
        for(int i=startIndex;i<candidates.size();i++){
            sum += candidates[i];
            path.push_back(candidates[i]);
            //递归,注意本身元素可重复,应当传入i而不是i+1!
            backtracking(path,result,candidates,target,sum,i);
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int>path;
        vector<vector<int>>result;
        int sum=0;
        int startIndex=0;
        backtracking(path,result,candidates,target,sum,startIndex);
        return result;
    }
};

剪枝优化

我们先看一下代码随想录里完整的树形结构:
在这里插入图片描述
组合问题的剪枝基本都是在for循环的搜索范围上做文章。

本题的剪枝操作是对candidates数组进行排序操作,如果前面的分支,例如[2,3]结果已经大于目标值,后面的[2,5]就没必要再搜索了!

也就是说,对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历

如图:

在这里插入图片描述
for循环剪枝代码:

for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++){}

剪枝重点就是当前遍历的candidates[i]+sum>target的时候,后面可以直接全部剪掉

剪枝修改完整版

class Solution {
public:
    void backtracking(vector<int>&path,vector<vector<int>>&result,vector<int>& candidates, int target,int sum,int startIndex){
        //终止条件
        if(sum>target){
            return;
        }
        if(sum==target){
            result.push_back(path);
            return;
        }
        //单层搜索
        for(int i=startIndex;i<candidates.size()&&candidates[i]+sum<=target;i++){
            sum += candidates[i];
            path.push_back(candidates[i]);
            //递归,注意本身元素可重复,应当传入i而不是i+1!
            backtracking(path,result,candidates,target,sum,i);
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int>path;
        vector<vector<int>>result;
        int sum=0;
        int startIndex=0;
        //直接库函数排序,sort默认从小到大
        sort(candidates.begin(),candidates.end());
        //传入排序好的
        backtracking(path,result,candidates,target,sum,startIndex);
        return result;
    }
};

补充:std::sort升降序的问题(默认升序)

注意,在C++中,std::sort函数默认的排序方式是从小到大,也就是升序排序。这个函数会对输入范围内的元素进行排序,使得排序后的元素序列是非递减的。

如果要实现降序排序,你可以给std::sort函数提供第三个参数,即一个自定义的比较函数或者lambda表达式。这个比较函数定义了两个元素的比较规则。

例如,如果想对candidates数组进行降序排序,你可以这样做:

std::sort(candidates.begin(), candidates.end(), std::greater<int>());

在这个代码中,std::greater<int>()是一个函数对象,它定义了一个规则,使得sort函数会按照这个规则进行排序,也就是降序排序

另一种方式是使用lambda表达式来定义比较规则:

std::sort(candidates.begin(), candidates.end(), [](int a, int b) {return a > b;});

在这个代码中,[](int a, int b) {return a > b;}是一个lambda表达式,它接受两个参数ab,如果a > b,则返回true,这样sort函数就会按照这个规则进行降序排序

40.组合总和Ⅱ

  • 本题最重要的一点是去重!并且本题只有树层去重树枝不能去重,此时要靠单独的次数统计数组来防止树枝也被去重

  • 防止访问vector数组下标-1越界,涉及到下标-1运算的都必须检查越界问题

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

注意:解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

思路

这道题和组合总和区别就在于:

  • 不包含本身。因为题目写的是candidates 中的每个数字在每个组合中只能使用 一次
  • candidates是有重复的!因此不能直接简单的把组合的写法改为递归i+1,还要进行去重操作!否则输出结果会出现一模一样元素的组合(详见debug测试),因为candidates后面还有和前面重复的元素
  • 用map或者set去重很容易超时,所以需要搜索的过程中就进行去重

同时,题目中说,candidates` 中的每个数字在每个组合中只能使用 一次 。这里很容易理解错误,实际上,candidates集合中本来就重复的元素只需要在树层里去重,树枝并不需要去重。可以看示例1的输出,包含了[1,1,6],说明本题是按照candidates前后两个1不一样来算的。

树层去重和树枝去重如下图所示。

在这里插入图片描述
类似上图,由于第一个1往后会取到所有的数字,所以第二个1往后取,一定会和第一个1发生重复。

但是树枝上有重复,是允许的,因为示例有组合出现数值相同的元素的例子。

最开始的写法

class Solution {
public:
    void backtracking(vector<int>&path,vector<vector<int>>&result,vector<int>& candidates,int sum,int target,int startIndex){
        //终止条件
        if(sum>target){
            return;
        }
        if(sum==target){
            result.push_back(path);
            return;
        }
        //单层搜素
        for(int i=startIndex;i<candidates.size();i++){
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracking(path,result,candidates,sum,target,i+1);
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
		vector<int>path;
        vector<vector<int>>result;
        int sum=0;
        int startIndex=0;
        backtracking(path,result,candidates,sum,target,startIndex);
        return result;
    }
};

debug测试:逻辑错误

在这里插入图片描述
仔细看一下输入,发现本题的candidates是有重复的!也就是说candidates里面,备选集合会有重复,但是结果不能有重复

用map或者set去重很容易超时,所以需要搜索的过程中就进行去重

修改完整版:去重

  • 去重需要传入一个used数组,来统计哪些元素被使用过。used数组的目的就是为了区分是树层还是树枝
  • 去重之前要做一个排序,使得重复的元素都放在一起,方便去重的时候进行判断!
  • 防止访问数组下标-1越界,涉及到下标-1运算的都必须检查越界问题
class Solution {
public:
    void backtracking(vector<int>&path,vector<vector<int>>&result,vector<int>& candidates,int sum,int target,vector<int>&used,int startIndex){
        //终止条件
        if(sum>target){
            return;
        }
        if(sum==target){
            result.push_back(path);
            return;
        }
        //单层搜索
        for(int i=startIndex;i<candidates.size();i++){
            //防止访问下标-1越界,涉及到下标-1的都必须检查越界问题
            if(i>=1&&candidates[i]==candidates[i-1]&&used[i-1]==0){
                continue; //直接不处理,跳到for循环的下一个
            }
            sum += candidates[i];
            path.push_back(candidates[i]);
            //记录use过当前的i
            used[i]=1;
            //开始递归
            backtracking(path,result,candidates,sum,target,used,i+1);
            //回溯,重置use
            sum -= candidates[i];
            path.pop_back();
            used[i]=0;
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
		vector<int>path;
        vector<vector<int>>result;
        //注意这种带有初始大小和初始值的vector数组定义方式!需要访问used下标所以必须初始化
        vector<int>used(candidates.size(),0);
        int sum=0;
        int startIndex=0;
        sort(candidates.begin(),candidates.end());
        backtracking(path,result,candidates,sum,target,used,startIndex);
        return result;
    }
};

used数组的作用,为什么不能删掉

used数组的作用就是用来区分上层的树层和树枝的。本题树层必须去重,而树枝不能去重

去重的逻辑:

if(candidates[i]==candidates[i-1]&&used[i-1]==0){
     //判断出i-1的used下标是0,也就是i-1并没有使用过,是树层而不是树枝!  
    continue;
}

我们继续以原数组[1,1,2]作为例子。

如果used[i-1]不为0,而是1的话,也就可能会是下图黄色笔标出的的情况:

在树枝里,递归到[1,1]这一层的时候,第二个1也满足candidates[i]==candidates[i-1],如果不用used,递归到下层开始把下层的i加入path的时候。第二个1的分支就会直接continue被跳过相当于整个[1,1]这一个分支都被剪掉了!此时会直接开始[1,2]这个分支,而原本的[1,1]分支作为树枝其实是被允许的,这个分支里的答案就漏掉了

本质上,也就是每一层递归,i都是那一层树对应的值

在这里插入图片描述
use需要重置的原因,本质上也是因为回溯回去的时候,需要把原来的元素拿出来,再放进去下一个元素。

debug测试:

1. Char 9: runtime error: reference binding to null pointer of type ‘int’ (stl_vector.h)

在这里插入图片描述
这个错误的发生是因为used没有初始化,而程序访问了used的下标。

“reference binding to null pointer of type ‘int’” 这个错误信息表示你试图访问一个int类型的空指针。在原版代码中,我们尝试在未初始化的used数组中使用下标操作符[]来访问或修改元素。int类型的空指针指的就是used数组为空。

used[i]=1used[i]=0试图访问used的某个位置,但是由于used的大小为0,所以这将导致运行时错误

应该在调用backtracking函数之前,used的大小设置为candidates的大小,并将所有元素初始化为0

vector<int>used(candidates.size(),0);

注意这种带有初始大小和初始值的vector数组定义方式!

2. Line 1034: Char 34: runtime error: addition of unsigned offset to 0x603000000070 overflowed to 0x60300000006c (stl_vector.h)

修改了used的初始化之后依旧报错,这个时候的问题出在i-1这里
在这里插入图片描述
i 等于0的时候,candidates[i-1]used[i-1]会试图访问数组的负索引,这是未定义的行为,可能导致运行时错误。

你需要确保在进行这种操作之前检查 i 是否大于0:

if(i > 0 && candidates[i]==candidates[i-1]&&used[i-1]==0)

总结

上面这种报错:**Char 34: runtime error: addition of unsigned offset to 0x603000000070 overflowed to 0x60300000006c (stl_vector.h)**是因为数组下标越界,或者数组下标被访问的时候没有初始化造成的。

在大多数情况下,这种类型的错误信息表示尝试访问了数组的某个位置,而这个位置不在数组的有效范围内,也就是说,你的代码试图进行越界访问。当试图访问未初始化的数组元素或者超出数组范围的下标时,都可能发生这种错误。

具体来看

runtime error: addition of unsigned offset to 0x603000000070 overflowed to 0x60300000006c (stl_vector.h)

这是一个运行时错误,它表示在执行到stl_vector.h文件中的某一行时,发生了溢出。具体来说,这是一个无符号整数溢出错误,说明程序尝试将一个正值(无符号偏移)加到一个地址上,但结果却比原地址小,这表明发生了溢出。

一般情况下,这可能是由于在访问vector的元素时使用了负索引,而在C++中,vector的索引必须是非负的,所以导致了越界错误。尤其是在这个代码中,如果 i 为0,那么 candidates[i-1]used[i-1] 就会尝试访问负索引,导致这个错误。所以必须在访问这些元素之前先检查 i 是否大于0。

而另一个报错Char 9: runtime error: reference binding to null pointer of type ‘int’ (stl_vector.h),是因为试图访问一个int类型的空指针

"int类型的空指针"是指在尝试访问或操作一个没有有效内存地址int 指针。这通常发生在试图访问或操作一个未初始化或者已经被销毁的指针。

vector的动态特性补充

std::vector 是一种动态数组它只为它的元素分配内存。如果你没有给 std::vector 添加任何元素,或者你试图访问超出它当前大小的下标,就会访问到无效的内存地址,从而引发错误。这就是为什么需要在使用 std::vector<int> 的下标访问元素之前,先确保它有足够的空间,并初始化它的元素。

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

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

相关文章

营销策划报告个人心得

营销策划报告个人心得篇1 20__年3月27日晚&#xff0c;我们12级市营专业的同学们早早的来到了大成楼。根据网络营销与策划课程实训要求&#xff0c;覃希老师邀请了校外企业经理给我们进行产品培训。 我们按实训项目分别安排在C302和C304教室培训&#xff0c;培训中同学们认认真…

基于深度学习的高精度水果检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度水果&#xff08;苹果、香蕉、葡萄、橘子、菠萝和西瓜&#xff09;检测识别系统可用于日常生活中或野外来检测与定位水果目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的水果目标检测识别&#xff0c;另外支持结果可视…

OpenGL 模板测试

1.示例效果图 选中模型对象&#xff0c;出现模型轮廓。 2.简介 当片段着色器处理完一个片段之后&#xff0c;模板测试会开始执行&#xff0c;和深度测试一样&#xff0c;它也可能会丢弃片段。接下来&#xff0c;被保留的片段会进入深度测试&#xff0c;它可能会丢弃更多的片…

MongoDB集群管理(三)

MongoDB集群管理 集群介绍 为什么使用集群 随着业务数据和并发量的增加&#xff0c;若只使用一台MongoDB服务器&#xff0c;存在着断电和数据风险的问题&#xff0c;故采用Mongodb复制集的方式&#xff0c;来提高项目的高可用、安全性等性能。 MongoDB复制是将数据同步到多个…

LNMP (Nginx网站服务) nginx 平滑升级

目录 1.1 Nginx的简介 1.2 Apache与Nginx的区别 Nginx对比Apache的优势&#xff1a; 1.3 Nginx的进程 Nginx的两个进程&#xff1a; 同步&#xff0c;异步&#xff0c;阻塞&#xff0c;非阻塞的概念补充 阻塞与非阻塞 同步和异步 2.1 编译安装Nginx 2.1 .1 关闭防火墙…

两点边值问题的有限元方法以及边值条件的处理示例

文章目录 引言题目补全方程刚度矩阵构造基底边值条件非齐次左边值条件非齐次右边值条件非齐次非齐次边值条件有限元方程 求数值解直接求总刚度矩阵先求单元刚度矩阵 引言 本文参考李荣华教授的《偏微分方程数值解法》一书 题目 对于非齐次第二边值问题 { − d d x ( p d u …

陶哲轩甩出调教GPT-4聊天记录,点击领取大佬的研究助理

量子位 | 公众号 QbitAI 鹅妹子嘤&#xff0c;天才数学家陶哲轩搞数学研究&#xff0c;已经离不开普通人手里的“数学菜鸡”GPT了&#xff01; 就在他最新解决的一个数学难题下面&#xff0c;陶哲轩明确指出自己“使用了GPT-4”&#xff0c;后者给他提出了一种可行的解决方法…

【FFmpeg实战】avformat_find_stream_info() 函数源码解析

转载自地址&#xff1a;https://cloud.tencent.com/developer/article/1873836 先来看一下 avformat_find_stream_info() 的头文件里的注释对该函数的介绍&#xff0c;本文我们基于 FFmpeg n4.2 版本的源码分析。 /*** Read packets of a media file to get stream informatio…

Apikit 自学日记:创建 API 文档

Apikit 中一共有5种创建API文档的方式&#xff1a; 新建API文档 导入API文档&#xff0c;详情可查看《导入、导出API文档》 从模板添加API文档&#xff0c;详情可查看《API文档模板》 自动生成API文档&#xff0c;详情可查看《自动生成API文档》 IDEA插件注释同步API文档 …

linux 在线安装 Redis

博主介绍&#xff1a; ✌博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家✌ Java知识图谱点击链接&#xff1a;体系化学习Java&#xff08;Java面试专题&#xff09; &#x1f495;&#x1f495; 感兴趣的同学可以收…

生成式AI掀起产业智能化新浪潮|爱分析报告

报告摘要 大模型支撑的生成式AI&#xff0c;让人类社会有望步入通用人工智能时代&#xff0c;拥有广阔的应用前景&#xff0c;有望赋能千行百业。当前生成式AI的落地整体处于初级阶段&#xff0c;不同模态的落地时间表差异明显&#xff0c;企业需求主要集中在数字化程度高、容…

地平线旭日x3派部署yolov8

地平线旭日x3派部署yolov8 总体流程1.导出onnx模型导出YOLOV8_onnxruntime.py验证onnxutils.py 2.在开发机转为bin模型2.1准备数据图片2.2转换必备的yaml文件2.3开始转换 3.开发机验证**quantized_model.onnx4.板子运行bin模型 资源链接 总体流程 1.导出onnx模型 导出 使用y…

03 | 事务隔离:为什么你改了我还看不见?

以下出自 《MySQL 实战 45 讲》 03 | 事务隔离&#xff1a;为什么你改了我还看不见&#xff1f; 隔离性与隔离级别 当数据库上有多个事务同时执行的时候&#xff0c;就可能出现脏读&#xff08;dirty read&#xff09;、不可重复读&#xff08;non-repeatable read&#xff0…

搜索功能全流程解析

在产品中一般会分布着大大小小的搜索&#xff0c;以便提升用户的信息获取效率和信息消费的能力。本文作者全流程角度&#xff0c;对搜索功能进行了讲解&#xff0c;并从搜索流程中寻找提升体验的触点&#xff0c;一起来看一下吧。 在产品中因多功能诉求和业务复杂性等因素&…

《Pytorch深度学习和图神经网络(卷 1)》学习笔记——第三章

学习基于如下书籍&#xff0c;仅供自己学习&#xff0c;用来记录回顾&#xff0c;非教程。 <PyTorch深度学习和图神经网络&#xff08;卷 1&#xff09;——基础知识>一书配套代码&#xff1a; https://github.com/aianaconda/pytorch-GNN-1st 百度网盘链接&#xff1a;…

vite优化

1.利用 rollup-plugin-analyzer 插件进行进行代码体积分析&#xff0c;从而优化你的代码。 根据项目体积分析&#xff0c;进行接下来的优化&#xff1a; &#xff08;一&#xff09;使用unplugin-vue-components插件按需加载antd vue 组件&#xff1a; 使用步骤 1、安装插件…

6.18 、Java初级:锁

1 同步锁 1.1 前言 经过前面多线程编程的学习,我们遇到了线程安全的相关问题,比如多线程售票情景下的超卖/重卖现象. 上节笔记点这里-进程与线程笔记 我们如何判断程序有没有可能出现线程安全问题,主要有以下三个条件: 在多线程程序中 有共享数据 多条语句操作共享数据 多…

GPT-4 的创造力全方位持平或碾压人类 | 一项最新研究发现

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 最近&#xff0c;一项有关 GPT-4 的创造力思维测试火了。来自蒙大拿大学和 UM Western 大学的研究团队发现&#xff0c;GPT-4 在 Torrance 创造性思维…

Sharding-JDBC之RangeShardingAlgorithm(范围分片算法)

目录 一、简介二、maven依赖三、数据库3.1、创建数据库3.2、创建表 四、配置&#xff08;二选一&#xff09;4.1、properties配置4.2、yml配置 五、范围分片算法六、实现6.1、实体层6.2、持久层6.3、服务层6.4、测试类6.4.1、保存订单数据6.4.2、根据时间范围查询订单 一、简介…

还在等待本地渲染?云渲染才是真的省时省心又省钱!

可能很多设计师会觉得本地渲染效果图或动画更灵活&#xff0c;而且没有什么额外的附加费用&#xff0c;但其实不然&#xff01;当你面对多个大型或紧急的项目时&#xff0c;本地渲染就“慌”了。 接下来我将全面对比“本地渲染”和“云渲染”&#xff0c;相信还在等待本地渲染…