【递归、搜索与回溯】记忆化搜索

记忆化搜索

  • 1.记忆化搜索
  • 2.不同路径
  • 3.最长递增子序列
  • 4. 猜数字大小 II
  • 5.矩阵中的最长递增路径

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.记忆化搜索

什么是记忆化搜索,下面我们以一道比较常见的509. 斐波那契数来演示一下什么是记忆化搜索。

关于这个斐波那契数,我们很早之前就碰到过如循环、递推、递归,不过它 还可以用动态规划,记忆化搜索来解决。矩阵快速幂也可以解决。

循环、递推、递归,时间复杂度O(N^2)
动态规划、记忆化搜索,时间复杂度O(N)
矩阵快速幂,时间复杂度O(logN)

因为记忆化搜索是基于递归代码来实现的,所以我们先用递归写这道题。

解法一:递归

在这里插入图片描述

dfs要干的事情就是给我一个数我把它第n个斐波那契数返回来。关于函数体怎么写,很简单求。F(0) = 0,F(1) = 1,F(n) = F(n - 1) + F(n - 2),其中 n > 1。求第n个斐波那契数先把n-1和n-2斐波那契数求出来。递归出口 n<=1 返回n就可以了。

int fib(int n)
{return dfs(n);
}int dfs(int n){if(n <= 1) return n;    return dfs(n-1)+dfs(n-2);}

我们先分析一下这个递归过程。当n=5的时候,我们看一下这个递归展开过程。就像一颗二叉树。有多少节点就要递归多少次。时间复杂度O(2^N)

在这里插入图片描述

我们先分析一下为什么这个递归它会这么慢。慢其实就慢在我们会重复计算一些问题,如d(3)我们会重复进入两次,但是这两个d(3)的递归展开树是完全一样的!这两个d(3)向上返回的值是完全一样的!
在这里插入图片描述
那我们想一下能不能这样优化一下,来一个备忘录,就是当我从d(5)来深度优先遍历的时候,先去左边,然后当我从d(3)这颗递归树返回时是一个返回值,(x为返回值)此时把这个d(3)=x这个f信息丢到备忘录里。(备忘录可能是一个数组、哈希表)。
然后当从左边回来然后去右边的时候,当我们又一次进入d(3)的时候,此时我就不把d(3)展开了。因为此时我去备忘录里找找我发现d(3)=x这个消息在左边就已经计算过了。所以在这里不展开了直接在备忘录里把x给拿出来,然后返回就可以了。

在这里插入图片描述
当有一个备忘录的时候,相同子树不在展开的时候,是不是就对递归做优化了。像这样一种方式就叫做记忆化搜索

解法二:记忆化搜索

在递归过程中,发现有一些完全相同的问题时,我们就可以把完全相同问题的结果放到备忘录里,然后递归进入相同问题的时候直接往备忘录里拿结果就可以了。这就是记忆化搜索,因此又称作带备忘录的递归。

此时你会发现其实并不止d(3)会被做优化其实就是剪枝,d(2)也会被优化。这么多分支都不用进去。时间复杂度直接从O(2^N)变成O(N)。所以添加一个备忘录可以极大优化我们的搜索过程。这也是记忆化搜索名字的由来,带着记忆去做dfs,这些重复的地方就不要重复进去了就实现了大量的剪枝,时间复杂度从指数级别降到线性级别!
在这里插入图片描述
如何实现记忆化搜索呢?

  1. 添加一个备忘录
  2. 递归每次返回的时候,将结果放到备忘录里面
  3. 在每次进入递归的时候,往备忘录里面瞅一瞅

添加一个什么样的备忘录呢?
紧盯这样一个原则,先找可变参数,然后将<可变参数,返回值>的映射关系存起来。

在这个dfs递归函数里。可变参数就是n,返回值就是第几个斐波那契数。所以在这里仅需<int,int> 前面是第几个斐波那契数的第几,后面是存的是具体的斐波那契数。

这个备忘录搞什么数据结构呢?
可以搞一个哈希表,这里我们可以来一个数组就行。这个数组可以搞成全局的,也可以当成dfs参数。

有时候备忘录可能需要初始化一下。搞成全局的备忘录里都是0,但是我们备忘录里有一个规则,备忘录里面初始存的值不能跟最终结果的值是一样的。 也就是说要去备忘录找这个值的时候,我得先判断一下你这个值是不是已经被计算过了,如果这个备忘录里面d(3)本来就存在X值,但是我还没有进入过d(3)里呢,就可能会导致误差。所有要把备忘录初始化为这个dfs里永远都不会出现得返回值!

在这里插入图片描述

int memo[31];int fib(int n)
{// 2.记忆化搜索//初始化memset(memo, -1 ,sizeof memo);// 1. 递归//return dfs(n);
}int dfs(int n)
{//往备忘录查找一下if(memo[n] != -1) return memo[n]; //剪枝if(n <= 1) {memo[n]=n;// 返回之前放进备忘录里面return n;}memo[n]=dfs(n-1)+dfs(n-2);// 返回之前放进备忘录里面return memo[n];
}

解法三:动态规划

动态规划我们一般思路是盯着5个方向。

  1. 确定状态表示
  2. 推导状态转移方程
  3. 初始化
  4. 确定填表顺序
  5. 确定返回值

解决动态规划问题现创建一个表格。可能是一维的也可能是二维的。我称为dp表。我们会赋予现赋予dp表一个含义。如果有一个i下标,我们会赋予dp[i] 一个含义。其中这个dp[i]的含义就是状态表示。推导这个状态转移方程就我想求这个dp[i]的值的时候,我们会从前面填的表格的值来推导dp[i]是多少。具体推导处理的公式就是状态转移方程初始化就是我们填dp[i]是依赖之前填的表格,但是0这个位置状态没有办法搞。因此必须先把0这个位置的值先初始化放后序填表。确定填表顺序 如果填dp[i]状态依赖于之前的状态,就必须是从左到右。确定返回值 根据题目要求确定最后返回的是这个表中那一个数。

其实我们可以从之前的递归和记忆化搜索直接推出这5步,因为它们是一一对应的关系。

dfs函数头就是给我一个数n我返回数n的斐波那契数。
填写备忘录的顺序,我们是做了深度优先遍历因此会一直递归下去,所以先会把d(1),dp(0)先放到备忘录里然后在往上返回一依次放。对应dp表就是从左到右。
主函数是dfs(n)调用的,对应dp表返回的就是dp[n]的值。

在这里插入图片描述

为什么动态规划和记忆化搜索这些都是一一对应的?因为动态规划和记忆化搜索本质都是一样的:

  1. 暴力求解(暴搜),动态规划要求dp[i]也要把前面都算出来,也是暴搜。无非就是动态规划和记忆化搜索是对暴搜的优化。
  2. 对暴力解法的优化是一样的:把已经计算过的值,存起来。 记忆化搜索算d(5),因为d(4)和d(3)已经放进备忘录里面了。直接去备忘录里找拿d(4)和d(3)就看可以。动态规划求dp[i]的时候是已经把前面dp[i-1]和dp[i-2]的值已经放到这个表里面了,然后求dp[i]的时候,直接去表里拿就就可以了。

《算法导论》这本书就是把记忆化搜索和常规的动态规划 归为 动态规划。无法就是记忆化搜索是递归形式的动态规划,而常规的动态规划是一个递推(循环)动态规划。

int dp[31];int fib(int n){// 3. 动态规划dp[0]=0,dp[1]=1;for(int i = 2; i <= n; ++i)dp[i] = dp[i - 1] + dp[i - 2];   return dp[n];}

下面我们总结几个问题。

1.所有的递归(暴搜、深搜),都能改成记忆化搜索吗?
不是的,只有在递归过程中,出现大量完全相同的问题时,才能用记忆化搜索的方式优化。

2.带备忘录的递归、带备忘录的动态规划、记忆化搜索 都是一回事

3.自顶向下 vs 自底向上 有什么不同。
无法就是解决一个问题时思考方向不同而已。自顶向下就是思考决策树时是按照从上往下的顺序来思考的,我想求d(5),我要先求出d(4)和d(3) 。。。。。自底向上就是从下往上思考,求d(5),我从最底下看,我先求d(0) d(1)推出d(2),然后在求d(3)。。。 而这两种方式就正好对应记忆化搜索和常规动态规划。记忆化搜索是递归加一个备忘录所以记忆化搜索方式就是从上往下。常规动态规划是递推方式,先求dp[0],自下往上对推导dp[n]是多少。

4.我们在解决这个问题的时候发现了一个流程,我可以先写出暴搜,然后改成记忆化搜索,然后把记忆化搜索东西抽象处理就是动态规划。好像发现解决动态规划问题的全新流程,暴搜->记忆化搜索->动态规划。以前是 常规动态规划
碰到一道动态规划的题先写成暴搜,然后改成记忆化搜索,在抽象成成动态规划。一般这样搞也没错,但是有些题你直接写出暴搜会比你用常规动态规划成本高的多。
暴搜->记忆化搜索->动态规划常规动态规划 都是解决动态规划的方式。
那个更好, 因人而异,因题而异。
在我看来暴搜可以为我们确定状态表示,提供一个方向。 而且记忆化搜索就已经是一个动态规划了,从暴搜改到记忆化搜索时间复杂度已经是线性级别了,没有必要在搞成动态规划了。

2.不同路径

题目链接:62. 不同路径

题目分析:

在这里插入图片描述
这个机器人位于左上角位置,每次只能向右和向下移动,问从开始位置到终点一共有多少种不同的路径。

我们下标从1开始计数,这样就少了很多边界情况。

在这里插入图片描述
算法原理:
接下来我们用记忆化搜索解决这个问题。如果纯记忆化搜索我们只要两步就可以了。第一步 先想出暴搜(递归)的代码。第二步 暴搜代码改成记忆化搜索,但是前提是能否改!

1.暴搜(递归)
我们最后向求出的m,n位置有多少种不同的走法,那么就这样搞dfs,dfs我给你两个参数,dfs(i,j) 你直接给我返回1,1到达i,j有多少种走法。具体dfs你内部怎么走我不关心,我相信你一定能够完成任务。接下来想想这个函数体 如何设计,我想从1,1到达这个三角的位置一共有多少种方式,其实只用关心两个位置就可以。因为想到达三角的位置,必须要从这两个圆圈到达,要求只能向右走向下走。
如果此时我知道到达圆圈有多少种方式那在多走一步就走到三角了。也就是说到达圆圈有多少种方式,到达这个三角也有多少种方式。因此仅需要达到两个圆圈有多少种方式加起来就是到达三角位置的方式 dfs(i-1,j) + dfs(i,j-1)

在这里插入图片描述
递归出口 我们考虑某个位置的时候,我们仅会考虑它上面的位置以及左边的位置。有可能i=1的时候去考虑i-1不就越界了吗,i=0的时候不能从非法位置达到这里。同理j=0也是一种非法情况,我们既要考虑上边也要考虑左边。因此
i == 0 || j == 0 return 0;
在这里插入图片描述
但还有一种隐藏递归出口,当i == 1 && j == 1的时候是位于起点的,上面和左边都没有所以需要特殊处理 i == 1 && j == 1 return 1

class Solution {
public:int uniquePaths(int m, int n) {return dfs(m,n);}int dfs(int i,int j){if(i == 0 || j == 0)return memo[i][j];if(i == 1 && j == 1) return 1;return dfs(i-1,j)+dfs(i,j-1);}
};

上面会超时,下面看看能否暴搜的代码改成记忆化搜索。在递归过程种发现大量重复的相同问题就可以用记忆化搜索。

记忆化搜索
我们发现在递归过程种发现大量重复的相同问题因此可以用记忆化搜索
在这里插入图片描述

  1. 搞一个备忘录
  2. 递归之前,查找一下备忘录
  3. 返回之前,把结果存在备忘录中

搞一个备忘录 上一道题是搞一个一维数组,但是这道题dfs函数里面是有两个可变参数,i和j一直在变化。里面的值是int,因此我可以搞一个int [m+1][n+1] 二维数组。因为要访问到m,n的位置。

class Solution {int memo[101][101];public:int uniquePaths(int m, int n) {// 记忆化搜索return dfs(m,n);}int dfs(int i,int j){if(memo[i][j] != 0) return memo[i][j];if(i == 0 || j == 0){memo[i][j] = 0;return memo[i][j];} //return 0;if(i == 1 && j == 1) {memo[i][j] = 1;return memo[i][j];}memo[i][j]=dfs(i-1,j)+dfs(i,j-1);return memo[i][j];}
};

解下来把记忆化搜索改成动态规划。

class Solution {int dp[101][101]; //先创建一个dp表 dp[i][j]表示到达i,j有多少种方式
public:int uniquePaths(int m, int n) {// 动态规划dp[1][1] = 1; // 初始化for(int i = 1; i <= m; ++i){for(int j = 1; j <= n; ++j){if(i == 1 && j == 1) continue;dp[i][j] = dp[i-1][j] + dp [i][j-1];//状态转移方程}}return dp[m][n];}
};

3.最长递增子序列

题目链接:300. 最长递增子序列

题目分析:

在这里插入图片描述

子序列是数组中可以不连续的的序列。让找的是最长的子序列。最长子序列不止一个但是返回的是最长的长度。

在这里插入图片描述
算法原理:
还是用记忆化搜索方式,分两步,第一步 暴搜,第二步 暴搜->记忆化搜索

暴搜
如何暴力的把最长递增子序列长度找出来,其实我们能够暴力的把所有递增子序列找到然后找到其中最长的长度不就可以了。如何暴力,很简单画个决策树,最长子序列可能回一任何一个起点开始,所以刚开始可能以2、5、3、7、101、18所有情况为起点,然后递归下去,如从2递归下去,只能从2后面的元素考虑,注意只能选择比2大的。选择5然后从5递归下去,只能选择比5大的等等。

接下来思考一下这个递归函数如何设计,函数头 我们看我们每次都是干的什么事情,每次我们都以某个位置为起点,找到以这个起点开始的最长递增子序列的长度。int dfs(pos)函数体 来一个for循环,让i从pos+1位置开始循环一直到n,因为考虑以pos位置为起点,接下来考虑的是pos+1位置,然后找到所有情况的最大值,然后在加上一个1就可以了,因为当前位置也要算上!for(int i=pos+1; i<n;++i) max(dfs(i)+1)。递归出口 不需要递归出口,把所有情况走完就返回了。

在这里插入图片描述
会超时间限制

class Solution {public:int lengthOfLIS(vector<int>& nums) {int n=nums.size();int ret=0;for(int i = 0; i < n; ++i){ret=max(ret,dfs(nums,i));}return ret;}int dfs(vector<int>& nums, int pos){int ret=1;//必须等于1,如果pos是最后一个,循环进不去,但它也是一个子序列,返回1才对.for(int i = pos + 1; i < nums.size(); ++i){if(nums[i] > nums[pos])ret=max(ret,dfs(nums,i)+1);}return ret;}
};

暴搜->记忆化搜索
存在大量完全相同的问题,可以改成记忆化搜索
在这里插入图片描述
还是那三步,这里就不写了。这里备忘录用的是局部的也是可以的

class Solution {public:int lengthOfLIS(vector<int>& nums) {int n=nums.size();vector<int> memo(n);int ret=0;for(int i = 0; i < n; ++i){ret=max(ret,dfs(nums,i,memo));}return ret;}int dfs(vector<int>& nums, int pos, vector<int>& memo){if(memo[pos] != 0) return memo[pos];int ret=1;//必须等于1,如果pos是最后一个,循环进不去,但它也是一个子序列,返回1才对.for(int i = pos + 1; i < nums.size(); ++i){if(nums[i] > nums[pos])ret=max(ret,dfs(nums,i,memo)+1);}memo[pos]=ret;return ret;}
};

记忆化搜索->动态规划
这里动态规划代码和之前的有点不一样。
首先来一个dp表,dfs函数->状态表示:dp[i]表示以i位置为起点的最长递增子序列长度。函数体-> 状态转移方程 :当求pos这个位置的时候,依赖的是pos后面的值,从里面找到最大然后在加+1。因此这个dp表填表顺序其实是从后往前填表。初始化都为1,自己本身也是一个子序列。返回值 返回的是dp表中最大值。

class Solution {public:int lengthOfLIS(vector<int>& nums) {// 动态规划int n=nums.size();int ret=0;vector<int> dp(n,1); //dp表+初始化//填表顺序: 从后往前for(int i = n-1; i >= 0; --i){for(int j = i + 1; j < n; ++j){if(nums[j] > nums[i]){dp[i]=max(dp[i],dp[j]+1);}}ret = max(ret,dp[i]);}return ret;}
};

4. 猜数字大小 II

题目链接: 375. 猜数字大小 II

题目分析:

在这里插入图片描述

从1到n中间选择一个数字,然后猜选的是几。猜到正确就会赢得游戏,猜错了会告诉你选择数字比你大还是比你小,然后继续猜。如果到这里结束以前的二分查找就可以了。但是还有条件。每当猜一个数字如果猜错了是要支付费用的,费用是本次猜错的数字。如果花光钱还没有猜中就会输掉游戏。然后要求的是,给一个数字n,不管选择的是n中的那一个数字,都必须保证你要能猜对并且花的钱还要最少!

最左边这幅图如果选10只要16块钱,因为到9了还说比9大仅需把9支付下来就行了,10是不用支付的,并且这个16块钱不仅能够保证到右边还能保证到左边所有数字。中间这幅图如果被猜的数字是10就必须保证21块钱,并且这21能够到达所有数字。同理最右边的图也一样。总之有很多种选择,但是一定要选择钱用的最少的。大家会发现这是一个暴搜,把所有情况都枚举出来然后把最少的钱返回去。

在这里插入图片描述

算法原理:

暴搜
假设n=10,刚开始我要选择第一个数,第一个数可以从[1,10]中任选一个数i做头结点。接下来会告诉你这个数选大了还是小了,然后就可以去左子树和右子树去选。然后重复的就来了,左子树去[1,i-1]任选一个a做头节点然后处理左子树右子树,右子树去[i+1,10]任选一个b做头节点然后处理左子树右子树。重复情况就是给我一个区间,我给你选一个头然后处理一下左子树和右子树。在所有的头i,注意i是在[1,10]变化的然后向上返回一个最小值。

在这里插入图片描述

接下来我们在细细看一下,当以i为根节点的时候处理完左子树和右子树,左右子树分别给我返回一个值假设是x和y。x代表左子树所有情况中最小值,y代表右子树所有情况中最小值。当在i的位置拿到x和y的时候,此时我要的是x和y中的最大值!因为要保证左右子树都能完胜,所以拿到左右子树中最大值那样左右子树都能完胜,然后还要在加上当前选的i自己本身。当把所有以i为根节点的情况都拿到了在取其中最小值就可以了。

因此dfs函数这样设计,给一个区间,然后把这个区间的保证胜利的最小值返回去。int dfs(1,10)函数体 从1到10枚举头结点,然后dfs枚举头结点的左区间和右区间,然后两者之间最大值然后加上i。然后把i=1、i=2。。。所有情况中取最小值,向上返回。递归出口 有两个递归出口,当left>=right。

在这里插入图片描述

class Solution {
public:int getMoneyAmount(int n) {return dfs(1,n);}int dfs(int left, int right){if(left >= right) return 0;//区间越界和碰到叶子节点int ret = INT_MAX;for(int head = left; head <= right; ++head){int l = dfs(left,head-1);int r = dfs(head+1, right);ret = min(ret, max(l,r)+head);}return ret;}};

暴搜->记忆化搜索
递归过程出现大量完全相同的问题,可以改成记忆化搜索
在这里插入图片描述

class Solution {int memo[201][201];
public:int getMoneyAmount(int n) {return dfs(1,n);}int dfs(int left, int right){if(left >= right) return 0;if(memo[left][right] != 0) return memo[left][right];int ret = INT_MAX;for(int head = left; head <= right; ++head){int l = dfs(left,head-1);int r = dfs(head+1, right);ret = min(ret, max(l,r)+head);}memo[left][right]=ret;return ret;}};

5.矩阵中的最长递增路径

题目链接: 329. 矩阵中的最长递增路径

题目分析:

在这里插入图片描述
给一个mxn的矩阵,里面有很多递增序列,返回里面递增序列的最长长度。

算法原理:

暴搜

让找最长递增序列长度,那就暴力枚举所有递增序列从中选择最长的。从第一个位置开始走,上下左右去找,然后返回这个位置上下左右中最长的,记住还要加自己本身1个。比如从1开始有2和6两种走法,从6返回的是2加起点最长递增是4,从2返回的是3加起来最长递增长度是4,所有1这个位置最长递增是4。然后还要和其他位置比,选择最长的。

在这里插入图片描述

class Solution {int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};int m,n;
public:int longestIncreasingPath(vector<vector<int>>& matrix) {m=matrix.size(),n=matrix[0].size();int ret=0;for(int i = 0; i < m; ++i){for(int j = 0; j < n; ++j){ret = max(ret,dfs(matrix, i, j));}}return ret;}int dfs(vector<vector<int>>& matrix, int i, int j){      int ret = 1;for(int k = 0; k < 4; ++k){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]){ret = max(ret, dfs(matrix, x, y)+1);}}return ret;}
};

暴搜->记忆化搜索
从1到6搜索和从6开始搜索情况是一样的。因此可以改成记忆化搜索。

class Solution {int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};int m,n;int memo[201][201];
public:int longestIncreasingPath(vector<vector<int>>& matrix) {memset(memo, -1, sizeof memo);m=matrix.size(),n=matrix[0].size();int ret=0;for(int i = 0; i < m; ++i){for(int j = 0; j < n; ++j){ret = max(ret,dfs(matrix, i, j));}}return ret;}int dfs(vector<vector<int>>& matrix, int i, int j){      if(memo[i][j] != -1) return memo[i][j];int ret = 1;for(int k = 0; k < 4; ++k){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]){ret = max(ret, dfs(matrix, x, y)+1);}}memo[i][j]=ret;return ret;}
};

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

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

相关文章

5000字深入讲解:企业数字化转型优先从哪个板块开始?

很多企业都知道数字化转型重要&#xff0c;但不知道应该怎样入手&#xff0c;分哪些阶段。以下引用国内领先数字化服务商 织信Informat 的数字化转型方法论材料&#xff0c;且看看他们是如何看待数字化转型的&#xff1f;数字化转型应该从哪先开始&#xff1f;如何做&#xff1…

P1107 [BJWC2008] 雷涛的小猫

[BJWC2008] 雷涛的小猫 题目背景 原最大整数参见 P1012 题目描述 雷涛同学非常的有爱心&#xff0c;在他的宿舍里&#xff0c;养着一只因为受伤被救助的小猫&#xff08;当然&#xff0c;这样的行为是违反学生宿舍管理条例的&#xff09;。在他的照顾下&#xff0c;小猫很快…

阿里云服务器数据库迁云: 数据从传统到云端的安全之旅(WordPress个人博客实战教学)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 一、 开始实战1.2创建实验资源1.3重置云服务器ECS的登录密码&#xff08;请记住密码&#xff09;1.4 设置安全组端口1…

Adobe Acrobat Pro或者Adobe Acrobat Reader取消多标签页显示,设置打开一个pdf文件对应一个窗口。

Windows系统&#xff1a;Adobe Acrobat Pro或者Adobe Acrobat Reader首选项-一般-取消在同一窗口的新标签中打开文档&#xff08;需要重启&#xff09;的对勾&#xff0c;点击确定&#xff0c;彻底关闭后重启&#xff0c;这样打开的每一个PDF文件对应的是一个窗口&#xff0c;并…

Bridging nonnull in Objective-C to Swift: Is It Safe?

Bridging nonnull in Objective-C to Swift: Is It Safe? In the world of iOS development, bridging between Objective-C and Swift is a common practice, especially for legacy codebases (遗留代码库) or when integrating (集成) third-party libraries. One importa…

重磅更新-UniApp自定义字体可视化设计

重磅更新-UniApp自定义字体可视化设计。 DIY可视化为了适配不同APP需要&#xff0c;支持用户自定义字体&#xff0c;自定义字体后&#xff0c;设计出来的界面更多样化&#xff0c;不再是单一字体效果。用户可以使用第三方字体加入设计&#xff0c;在设计的时候选择上自己的字体…

AI副业赚钱攻略:掌握数字时代的机会

前言 最近国产大模型纷纷上线&#xff0c;飞入寻常百姓家。AI副业正成为许多人寻找额外收入的途径。无论您是想提高家庭收入还是寻求职业发展&#xff0c;这里有一个变现&#xff0c;帮助您掌握AI兼职副业的机会。 1. 了解AI的基础知识 在开始之前&#xff0c;了解AI的基础…

一个开源的、独立的、可自托管的评论系统,专为现代Web平台设计

大家好&#xff0c;今天给大家分享的是一个开源的、独立的、可自托管的评论系统&#xff0c;专为现代Web平台设计。 Remark42是一个自托管的、轻量级的、简单的&#xff08;但功能强大的&#xff09;评论引擎&#xff0c;它不会监视用户。它可以嵌入到博客、文章或任何其他读者…

kafka-Stream详解篇(附案例)

文章目录 Kafka Stream 概述Kafka Stream 概念Kafka Stream 数据结构入门案例一需求描述与分析配置KafkaStream定义处理流程声明Topic接收处理结果发送消息测试 入门案例二需求描述与分析定义处理流程接收处理结果声明Topic 更多相关内容可查看 Kafka Stream 概述 Kafka Strea…

深入理解linux shell 中的exec内置命令ubuntu bash

概览 每当我们在Bash shell 中运行任何命令时&#xff0c;默认情况下都会创建一个子 shell&#xff0c;并生成&#xff08;分叉&#xff09;一个新的子进程来执行该命令。但是&#xff0c;当使用 exec时&#xff0c;exec 后面的命令将替换当前 shell。这意味着不会创建任何子 …

【高中数学之基本不等式】已知:x,y皆为正实数且x+3y=5xy,求3x+4y的最小值?

解&#xff1a;此题若使用“化二为一”法&#xff0c;会发现分母中出现了5x-3或是5y-1这样的式子&#xff0c;它可能导致负数的出现&#xff0c;已经违反了基本不等式中a,b皆大于零的应用条件。 此时应该迷途知返&#xff0c;及时易辙改弦。 此题适用“乘一法”&#xff0c;这…

第1章_搭建开发环境

文章目录 第1章 搭建开发环境1.1开发套件硬件接口资源介绍1.2资料下载1.3安装Keil MDK1.3.1**软件下载**1.3.2**软件安装**1.3.3 PACK 安装 1.4 安装 STM32CubeMX1.5 安装 STM32CubeProgrammer1.6 安装 ST-Link 驱动1.7 安装 CH340 驱动 第1章 搭建开发环境 1.1开发套件硬件接…

Redis基础教程(一):redis配置

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

星光云720全景VR系统源码

星光云720全景VR系统源码 系统体验地址项目介绍JDK版本后端主要依赖前端框架前端node 版本用户端框架介绍技术选型依赖全景内容简介系统图片部分功能截图系统体验地址 系统体验地址 VR全景系统体验地址 账号&#xff1a;18175760278 密码&#xff1a;12345678 项目介绍 JDK版…

图鸟模板-官网:基于Vue 3的前端技术探索与实践

摘要&#xff1a; 随着Web技术的不断发展&#xff0c;前端开发已经从简单的页面展示向功能丰富、交互体验优良的方向发展。Vue.js作为一款轻量级且功能强大的前端框架&#xff0c;自推出以来就受到了广泛的关注和应用。特别是Vue 3的发布&#xff0c;更是为前端开发带来了诸多新…

机器学习笔记 人脸识别技术全面回顾和小结(1)

一、简述 人脸识别是视觉模式识别的一个细分问题。人类一直在识别视觉模式&#xff0c;我们通过眼睛获得视觉信息。这些信息被大脑识别为有意义的概念。对于计算机来说&#xff0c;无论是图片还是视频&#xff0c;它都是许多像素的矩阵。机器应该找出数据的某一部分在数据中代表…

最近公共祖先(倍增,tarjan,树链剖分)

两个点的最近公共祖先&#xff0c;即两个点的所有公共祖先中&#xff0c;离根节点最远的一个节点。 倍增算法 1.dfs一遍&#xff0c;创建ST表 2.利用ST表求LCA 内容来源 D09 倍增算法 P3379【模板】最近公共祖先&#xff08;LCA&#xff09; #include<iostream> #in…

特斯拉下一代自动驾驶芯片的深度预测

引言 特斯拉一直以来都在自动驾驶技术上不断突破&#xff0c;随着AI大模型技术的爆发&#xff0c;其下一代自动驾驶芯片&#xff08;HW5.0&#xff09;也备受瞩目。本文将深入分析和预测特斯拉下一代自动驾驶芯片AI5的技术特点及其对行业的影响。 深入技术分析 现有自动驾驶…

React小记(五)_Hooks入门到进阶

React 16.8 版本 类组件 和 函数组件 两种组件共存&#xff0c;到目前 React 18 版本&#xff0c;官方已经不在推荐使用类组件&#xff0c;在函数组件中 hooks 是必不可少的&#xff0c;它允许我们函数组件像类组件一样可以使用组件的状态&#xff0c;并模拟组件的生命周期等一…

高效数据采集监控平台 一体化平台 数据可视化!

提高工作效率&#xff0c;一直是各种厂家在寻找的方法。任何一种有效且实用的方法都值得去尝试。数据采集监控平台是一种能高效处理数据的方式&#xff0c;其主要工作内容是从各个产生数据的仪器设备传感器中采集数据、对数据进行集中整理整合、分析、显示、绘制图表、存储、传…