当前位置: 首页 > news >正文

动态规划入门

前言:首先,什么是动态规划?

      动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

      我们都熟知的斐波那切数列问题就是典型的动态规划问题,后面的问题的解,与前面已经求出来的问题的解有关,也就是我们经常在递归中用公式   f[i]=f[i-1]+f[i-2](i>2),在动态规划中,我们也把这种类型的公式叫做状态转移方程。

      我们的动态规划算法,本质上是由dfs而来,经历了dfs->记忆化搜索->动态规划的过程,可以说,动态规划就是dfs的提高。

1.典例引入:

爬楼梯(斐波那切数列的应用)

 这就是典型的斐波那切数列问题,我们跳到第i级台阶有两种可行方案,第一种是从第i-1级台阶上跳一步,另一种是从第i-2级台阶跳两步,而第i-1级台阶和第i-2级台阶也分别可以由两种不同的方案,当然我们需要知道前两个初始值,以便让我们开始循环求解方案数,而我们易知第一级台阶只有一种方案,第二节台阶有两种方案,从第三极台阶开始我们就可以通过递归来求解了,当然,这个例子想必自然不用多说,但凡学过递归的,肯定都是老生常谈的例子了,这里直接给出代码,给后续的例题提供模板。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20;
int n;
int dfs(int n)
{if (n == 1)return 1;else if (n == 2)return 2;elsereturn  dfs(n - 1) + dfs(n - 2);
}
int main()
{cin >> n;cout << dfs(n) << endl;return 0;
}

1.1从dfs到记忆化搜索再到动态规划的优化算法

上面的斐波那切数列数列式的问题,在数据不大的前提下我们的dfs是可行的,但是,我们知道dfs的执行过程实际上是极为复杂的,这中间多次重复的调用已经求出的值,我们可以进一步优化算法,把子问题的答案存起来,去掉重复值;

1.1.1 记忆化搜索

所谓记忆化搜索,就是将已经求出的子问题的答案保存在数组中,当我们在求解一个问题是,先查找数组中是否已有答案,查到直接返回数组值,没查到再进行递归求解,这样可以省去重复的分支,优化时间复杂度。(这里不考虑数据溢出的问题,事实上,当我们的n为50的时候就已经爆int了~~

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n;
int mem[maxn];
int dfs(int n)
{if (mem[n])return mem[n];int sum = 0;if (n == 1)sum = 1;else if (n == 2)sum = 2;elsesum = dfs(n - 1) + dfs(n - 2);mem[n] = sum;return sum;
}
int main()
{cin >> n;memset(mem, 0, sizeof(mem));cout << dfs(n) << endl;return 0;
}

1.1.2 动态规划

我们的动态规划实际上就是记忆化搜索的改进,本质上还是递归,递(把一个大问题分解成一个个子问题,相当于自顶向下求解)归(由最小子问题答案带回求出大问题答案,相当于自底向上返回大问题答案),动态规划,就是省去“递”的过程,直接从已知子问题开始,逐步求解大问题,直到求出大问题的解。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n;
int f[maxn];//sum = dfs(n - 1) + dfs(n - 2);int main()
{cin >> n;f[1] = 1;f[2] = 2;if(n==1||n==2){cout<<n<<endl;return 0;}for (int i = 3; i <= n; i++)f[i] = f[i - 1] + f[i - 2];cout << f[n] << endl;return 0;
}

*1.1.3三变量的再优化(空间复杂度的优化)

在上述的递推过程中我们不难发现,f[i]的取值只和f[i-1]还有f][i-2]有关,我们可以直接用三个变量来代替整个数组来优化空间,这个并不难能想到,这里只是一提,事实上,在一些竞赛等方面,时间复杂的才是更重要的考察点。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n;
int ans = 0;
int main()
{cin >> n;if (n == 1 || n == 2){cout << n << endl;return 0;}int f1 = 1, f2 = 2;for (int i = 3; i <= n; i++){ans = f1 + f2;f1 = f2;f2 = ans;}cout << ans << endl;return 0;
}

2.渐入佳境

相信你可能已经跃跃欲试了,这不就来了嘛

 

 这个题可能刚开始并不容易想到思路,因为这并不像前面的爬楼梯一样是简单的方案数相加问题了,这是一道求最优解的问题,我们来捋下思路,

那我,我们现在就可以很容易的写出dfs版的代码:

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int ans = 0;
int w[maxn];//表示店铺的价值
int dfs(int x)//x表示第x家店铺
{if (x > n)return 0;elsereturn max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺
}
int main()
{cin >> T;while (T--){cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];int ret=dfs(1);cout << ret << endl;}return 0;
}

上面的代码很存在很多的重复性的子分支存在,很遗憾的,这段代码会TLE的,那我在我们之前的优化中,就要采用记忆化搜索和动态规划来优化时间了,

2.1.记忆化搜索改进

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int ans = 0;
int w[maxn];//表示店铺的价值
int mem[maxn];
int dfs(int x)//x表示第x家店铺
{if (mem[x])return mem[x];int sum = 0;if (x > n)sum = 0;elsesum= max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺mem[x] = sum;return sum;
}
int main()
{cin >> T;while (T--){memset(mem, 0, sizeof(mem));cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];int ret=dfs(1);cout << ret << endl;}return 0;
}

2.2动态递归

我们在一般的竞赛中,记忆化数组还是不常用的,事实上,上一个题的记忆化搜索已经可以AC了,我们还是来搞一下比较常用的动态规划

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int w[maxn];//表示店铺的价值
int f[maxn];//max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺int main()
{cin >> T;while (T--){memset(f, 0, sizeof(f));cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];for (int i = n; i >= 1; i--){f[i] = max(f[i+1], f[i + 2] + w[i]);//由底层求到根节点}cout << f[1] << endl;}return 0;
}

很多人可能不理解为什么要逆序来进行递推,说实话,一开始我也不太理解,但是我们来看,在上面的二叉树类型的结构中,我们需要根据上面所求得的答案来推出下面的最优解,也就是相当于最小的已知子问题是我先把第一个元素作为条件开始进行递归的,第一个子问题的答案我们是知道的,动态规划的本质就是从大的未知问题向小的已知的子问题的“归”操作,并且我们的递归过程一定是从大问题到小问题的“回溯”过程中才能得出答案的,假如我们用的是正序,那么状态转移方程就要变变形式了。

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int w[maxn];//表示店铺的价值
int f[maxn];//max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺int main()
{cin >> T;while (T--){memset(f, 0, sizeof(f));cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];//for (int i = n; i >= 1; i--)//{//	f[i] = max(f[i+1], f[i + 2] + w[i]);//由底层求到根节点//}//cout << f[1] << endl;for (int i = 1; i <= n; i++){//f[i] = max(f[i - 1], f[i - 2] + w[i]);//因为i为1时数组会越界,所以我们同时让下标加2f[i+2] = max(f[i+1], f[i] + w[i]);//由根节点求到叶子结点}cout << f[n+2] << endl;}return 0;
}

可见两种形式的状态转移方程虽然不相同,但是两者的本质都是通过大问题像小问题递归进行的求解。

3.典例1

 

 题目并不难理解(看好了哈,这个不是那个有向左走次数与向右走次数的差不超过1的那个),他是想让我们找出来一条最长路径,结合我们的前面的讲解,我们不难给出下面的dfs代码:

#include <bits/stdc++.h>using namespace std;
const int maxn = 105;
int n;
int mp[maxn][maxn];//表示店铺的价值int dfs(int x, int y)
{if (x > n||y>n)return 0;elsereturn max(dfs(x+1,y),dfs(x+1,y+1)) + mp[x][y];
}
int main()
{cin >> n;for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> mp[i][j];cout << dfs(1, 1) << endl;return 0;
}

看看数据量,1000行,dfs肯定会崩掉的,我们只能来优化,

记忆化搜索的方法我这里就不展开写了,毕竟模板在那挺好写的,我们直接来看重头戏动态规划

3.1动态规划法降低时间复杂度

我们先来画出递归树:

从以上来看,我们的递推方程也会有正推和逆推两种方式,这两种方式的不同之处在于:

对于正推(也就是从上往下推)

      我们现在已知的子问题是最上层的7这个点,因为它只有一个选择,我们需要每次为该层的子问题选择最优解,我们知道从上往下看,要求是选右下或者正下(在二维数组中是这样的,在该图中就是左下),那么相当于我们在二维数组中,我们向最大层靠近(向下层)的过程中,就要每次选择上层的正上方的数或者左上方的数,然后取他们的最大值即可,当我们取到最后一行时,因为我们是for循环遍历,所以每个数都会遍历到,也就造成了我们的第n行每一列都会有一个最大值,我们无法确定最后一行哪一列才是最大值,所以最后还需要求出最大的那个数

对于逆推

     逆推则是从第n行出发,我们每次寻找的是使我们当前这一层能取到最,我们的最终结果应该汇聚到最顶部的根节点上,所以我们应该把每层的结果放到上一层中去,逐层往上送至顶点处,所以我们对于每个点的选择应该还是遵从选择右下或者正下的,也就是说,我们应该使起点处为该节点所对应的值(比如最后一行的第一个节点对应的值应该为4),所以虽然是向上遍历,但是我们的选择还是来自下方的两个数,这样既保证了我们第n行的n个起点的值为节点值,也保证了我们选择最大值得目的。

这是代码:

#include <bits/stdc++.h>using namespace std;
const int maxn = 105;
int n;
int mp[maxn][maxn];//表示店铺的价值
int f[maxn][maxn];int main()
{cin >> n;for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> mp[i][j];//正推for(int i=1;i<=n;i++)for (int j = 1; j <= i; j++){f[i][j] = max(f[i -1][j], f[i - 1][j - 1]) + mp[i][j];}//这里我们需要注意的是,我们一次所求出的f[i][j]只是相对的第n行其中一个位置的加和最大值,也就是说我们在这次循环结束后并不能马上求出最大值,我们的最大值在第n行中,但是我们不能确定在第几列,所以我们要排序寻找sort(f[n]+1, f[n] + n+1);cout << f[n] << endl;//逆推//for (int i = n; i >= 1; i--)//	for (int j = 1; j <= i; j++)//		f[i][j] = max(f[i + 1][j], f[i + 1][j + 1]) + mp[i][j];我们的终点只有一个,所以说递推的终点只有一个//cout << f[1][1] << endl;return 0;
}

间章规律小总结(不知对否,还请慎读)

       从上面的例子中我们不难的发现,动态规划本质上就是递归加递推的过程,一边递归一遍利用递推公式求解上层问题,其中递归的过程体现在for循环中,递推的过程体现在状态转移方程中,还记得我开头提过的动态规划在“归”的过程中出结果吗,准确的来说是在递推的过程中出结果,我们都知道“递”和“归”是两个相反的过程,同样的,递归和递推也是如此,细心地朋友可能会发现,在上面的两种正逆序的不同遍历过程中,实际上他们所对应的递推公式也是相反的,直白的说,for循环和状态转移方程是相反的,在我们上面的典例1中,for循环由上向下遍历,那么状态转移方程的每一个状态就和前一个状态有关,反之,如果是逆序遍历,相当于递归方向由下到上,那么递推就要与其相反才能有效的求出结果从而退出递归状态,因而每个点的状态由他的后一个状态求出,(如果我嘟囔错了,还请给位大佬帮忙指正~~多谢)。

By the  way:前面的f表示的二维数组其实可以直接优化成一维数组,我们只需要保留上一层的对应的状态即可(别试正推,因为不行~QAQ)

 但是这个要慎用,一般不推荐,搞不好正逆推会出错的,写这个只是为了知道一些大佬写题解会用一维数组就行了。

 

4.最后一站:让我们来到经典背包问题

输入样例:

4 5
1 2
2 4
3 4
4 5

 输出样例:

8

这个问题可能和前面的大盗阿福类似,还是在限制条件下求解最优解问题,我们老规矩,先来写经典的dfs版本:

#include <bits/stdc++.h>using namespace std;
const int maxn = 1005;
int N,V;
int w[maxn], v[maxn];
int dfs(int x, int surV)//surV表示当前背包的剩余体积,x表示当前正在考虑第x个物品
{if (x > N)return 0;if (v[x] > surV)//该物品超出当前背包剩余体积,装不下,被迫继续寻找下一个物品return dfs(x + 1, surV);else if(surV>=v[x]){//能装下,但是我们可以自由选择装与不装return max(dfs(x + 1, surV - v[x])+w[x], dfs(x + 1, surV));}
}
int main()
{cin >> N >> V;for (int i = 1; i <= N; i++)cin >> v[i] >> w[i];cout << dfs(1, V) << endl;//从第一个物品开始考虑return 0;
}

下面还是直接看动态规划版本:记忆化搜索就是个模板,不是太难理解,而且和dfs差距不大。

建议还是先理解前面正逆序方式的不同:

#include <bits/stdc++.h>using namespace std;
const int maxn = 105;
int N,V;
int dp[maxn][maxn];//第一个下标表示背包的当前选择的物品,第二个下标为当前背包剩余体积
int w[maxn], v[maxn];
int dfs(int x, int surV)
{if (x > N)return 0;if (v[x] > surV)//该物品超出当前背包剩余体积,装不下,被迫继续寻找下一个物品return dfs(x + 1, surV);else if(surV>=v[x]){//能装下,但是我们可以自由选择装与不装return max(dfs(x + 1, surV - v[x])+w[x], dfs(x + 1, surV));}
}
int main()
{cin >> N >> V;for (int i = 1; i <= N; i++)cin >> v[i] >> w[i];倒推//for (int i = N; i >= 1; i--)//{//	for (int j = 0; j <= V; j++)//	{//		if (j < v[i])//			dp[i][j] = dp[i + 1][j];//		else if(j>=v[i])//		    dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - v[i]] + w[i]);//看到没,和我说的规律一样//	}//}// cout<<dp[1][V]<<endl;//终点在第一个物品//正推for(int i=1;i<=N;i++)for (int j = 0; j <= V; j++){if (j < v[i])dp[i][j] = dp[i - 1][j];else if (j >= v[i])dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);//看到没,和我说的规律一样}cout << dp[N][V] << endl;//终点在第N个物品return 0;
}

5.总结

       动态规划是一个上下限都极高的算法,想要学会并不容易,不知我这七千五百字能够让你们学到多少,动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其他的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间。

6.金句省身

       不要期待突如其来的惊喜,更不要期待命运会突然得到改变。没有一蹴而就的成功,好运不过是努力的伏笔而已。真正让我们成长的力量,就藏在那些日复一日看似平常的坚持里。所以,不要抱怨,你只管努力,该来的成功不会远。

                                                                                                          ----------   摘自《人民日报》

 

相关文章:

动态规划入门

前言&#xff1a;首先&#xff0c;什么是动态规划&#xff1f; 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中&#xff0c;可能会有许多可行解。每一个解都对应于一个值&#xff0c;我们希望找到具有最优值的解。动态规划算法与分治法类似&#xff0c;其基本思…...

day26 SpringBootWeb案例(二)

目录 SpringBootWeb案例 1. 新增员工 1.1 需求 1.2 开发 1.3 测试 2. 文件上传 2.1 简介 2.2 本地存储 2.3 阿里云OSS 3. 配置文件 3.1 Value 3.2 yml配置 3.3 ConfigurationProperties 4. 修改员工 4.1 需求 4.2 查询回显 4.3 保存修改 SpringBootWeb案例 前…...

力扣-《剑指offer》-哈希表-刷题笔记

目录 第一题&#xff1a;03.数组中重复的数字 第二题&#xff1a;39.数组中出现次数超过一半的数字 第三题&#xff1a;50.第一个只出现一次的字符 第四题&#xff1a;53. &#xff08;0-n-1&#xff09;中缺失的数字 第一题&#xff1a;03.数组中重复的数字 我的答案&…...

【SpringBoot】| 邮箱发送验证码,你会了吗?

目录&#x1f981; 题外话&#x1f981; 提前准备2.1 配置邮箱第三方登录2.1.1 点击设置——账户2.1.2 开启POP3/SMTP服务2.2 添加依赖2.3 yaml配置&#x1f981; 进入主题&#x1f981; 测试使用&#x1f981; 尾声3.1 安利一个生成验证码的工具类3.1.1 添加依赖3.1.2 编写配置…...

Linux系统安装部署及配置Grafana

TOC 用于 UI 展示 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpm1 安装 grafana 1.1 下载安装 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpmsudo yum install grafana-8.0.3-1.x86_64.rpm1.2 启动&状态查看 sudo syst…...

Python3 入门教程||Python3 输入和输出||Python3 File 方法

Python3 输入和输出 在前面几个章节中&#xff0c;我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python 两种输出值的方式: 表达式语句和 print() 函数。(第三种方式是使用文件对象的 write() 方法; 标准输出文件可以…...

有效的字母异位词(力扣刷题)

给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2: 输入: s "rat", t "car" 输出: false 说明: 你可以假设字符串只包含小写字母。 …...

73、介绍下 HashMap 的底层数据结构

73、介绍下 HashMap 的底层数据结构 我们现在用的都是 JDK 1.8&#xff0c;底层是由“数组链表红黑树”组成&#xff0c;如下图&#xff0c;而在 JDK 1.8 之前是由“数组链表”组成。 1.Hash Hash叫做”散列表“&#xff0c;就是把任意长度的输入&#xff0c;通过散列算法&am…...

系统集成路由器OSPF动态、综合路由配置

实验任务&#xff1a;动态路由协议RIP、OSPF协议的内容和特点动态路由RIP、OSPF实验&#xff0c;建立拓扑pc1>>R1>>R2>>R3>>pc2&#xff0c;使pc1与pc2能相互通信&#xff0c;并配置PC端静默接口。熟悉配置vlan间路由技术&#xff1a;多层交换机虚拟接…...

【力扣周赛 338】

6354. K 件物品的最大和 - 力扣&#xff08;Leetcode&#xff09;袋子中装有一些物品&#xff0c;每个物品上都标记着数字 1、0或 -1。给你四个非负整数 numOnes、numZeros、numNegOnes和 k。袋子最初包含&#xff1a;numOnes 件标记为 1 的物品。numZeroes 件标记为 0 的物品。…...

大数据Flink进阶(八):Apache Flink架构介绍

Apache Flink架构介绍 一、Flink组件栈 在Flink的整个软件架构体系中,同样遵循这分层的架构设计理念,在降低系统耦合度的同时,也为上层用户构建Flink应用提供了丰富且友好的接口。...

Mars3d项目启动上的一些坑

前言 最近新入职了一家公司&#xff0c;公司新开了有个未来城市的项目&#xff0c;需要用到3D城市建模&#xff0c;公司老总选了Mars3d作为前端框架&#xff0c;项目分给我了&#xff0c;又是一个全新的领域&#xff0c;开搞吧&#xff01; 下面是自己遇到的几个小问题&#x…...

通俗易懂【Springboot】 单文件下载和批量下载(多个文件合成一个压缩包下载)

文章目录一.单文件下载1.简单理解文件下载2.单文件下载的具体代码实现3.测试4.单文件下载整体代码二.多文件批量下载&#xff08;多个文件合成一个压缩包下载&#xff09;1.多文件下载的实现方式&#xff0c;这里使用了ZipOutputStream2.具体代码实现3.测试4.文件批量下载&…...

CnOpenData中国行政区划shp数据

一、数据简介 中国行政区划数据是重要的基础地理信息数据&#xff0c;目前不同来源的全国行政区划数据非常多&#xff0c;但能够开放获取的高质量行政区域数据少之又少。基于此&#xff0c;锐多宝的地理空间制作一套2013-2023年可开放获取的高质量行政区划数据。该套数据以2022…...

GPT-4零失误通关大厂模拟面试,offer拿到手软?与AGI首次接触

来源: FoxyearMeta “GPT-4可被视作AGI &#xff08;通用人工智能&#xff09;的早期版本。” 如若从他人口中说出&#xff0c;或许是无稽之谈—— 但是由微软雷蒙德研究院机器学习理论组负责人万引大神Sbastien Bubeck与2023新视野数学奖得主Ronen Eldan、2023新晋斯隆研究奖得…...

Hardhat 环境搭建及教程示例

一.安装node.js curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash nvm install 18 nvm use 18 nvm alias default 18 npm install npm --global # Upgrade npm to the latest version 二. 安装hardhat 2.1 创建hardhat安装目录 mkdir hard…...

复杂链表的复制-剑指Offer35-java

一、题目描述 请实现 copyRandomList 函数&#xff0c;复制一个复杂链表。在复杂链表中&#xff0c;每个节点除了有一个 next 指针指向下一个节点&#xff0c;还有一个 random 指针指向链表中的任意节点或者 null。 示例 1&#xff1a; 输入&#xff1a;head [[7,null],[13,…...

【Linux】进程理解与学习Ⅰ-进程概念

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅&#x1f339;相关文章推荐&#xff1a;【Linux】冯.诺依曼体系结构与操作系统进程概念什么是进程&#xff1f;进程是什么&#xff1f;我们打开任务管理器可以看到有…...

WebKitX ActiveX 6.0 X86 Crack

WebKitX ActiveX将 Chromium Embedded Framework (CEF3) 包装到一个进程外的 ActiveX 组件中&#xff0c;以便与 OLE/COM 语言一起使用。Chromium Embedded Framework 封装了 WebKit Blink HTML5 Renderer 和 Google V8 JavaScript Engine。这是一个用于商业用途的生产级稳定组…...

开源项目:数据库表结构生成文档工具

目录 一、软件介绍 二、技术框架 三、功能介绍 四、代码展示 1、获取数据库信息部分代码 2、导出Html文档代码 五、运行效果 六、项目开源地址 一、软件介绍 今天给大家分享我自己编写的数据库表结构文档生成工具&#xff0c;方便大家在实际开发当中&#xff0c;可以很方便导出…...

spring的两种拦截器HandlerIntercepter和MethodIntercepter

介绍 Spring有两种拦截器提供给我们使用&#xff0c;一种是HandlerIntercepter&#xff0c;另一种是MethodIntercepter。这两种的来源不同&#xff0c;实现方式也不同&#xff0c;具体的下面来看一下。 HandlerIntercepter 来源 来源于spring-webmvc包 HandlerIntercepter拦…...

初级算法-字符串

主要记录算法和数据结构学习笔记&#xff0c;新的一年更上一层楼&#xff01; 初级算法-字符串一、反转字符串二、反转字符串&#xff08;二&#xff09;三、替换空格四、翻转字符串里的单词五、左旋转字符串六、实现 strStr()七、重复的子字符串字符串中元素只能是字符String…...

华为OD机试题 - 寻找目标字符串(JavaScript)| 机考必刷

更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为…...

删除Terminating状态的namespace:cattle-system

这里以cattle-system为例&#xff01;执行删除命令后namespace&#xff08;也是用其他k8s object&#xff09;仍然存在&#xff0c;首先执行 kubectl edit namespace cattle-system 查看是否存在spec.finalizers: kubernetes&#xff0c;如&#xff1a; spec: finalizers:…...

MiniOB 并发B+树实现解析

MiniOB 是 OceanBase 联合华中科技大学推出的一款用于教学的小型数据库系统&#xff0c;希望能够帮助数据库爱好者系统性的学习数据库原理与实战。 B 树介绍 B 树是传统数据库中常见的索引数据结构&#xff0c;比如MySQL、PostgreSQL都实现了B树索引。B 树是一个平衡多叉树&am…...

SpringCloud负载均衡服务调用——Ribbon

Ribbon 本专栏学习内容来自尚硅谷周阳老师的视频 有兴趣的小伙伴可以点击视频地址观看 简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 简单的说&#xff0c;Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算…...

各种邮箱服务软件对比

1.宝塔邮局管理器 特点:简单易用,可视化操作,小白也能搞,还有备份功能,一般足够用了 缺点:稳定性真是差,隔三差五的不能收发.没有接口,不能任意修改邮箱密码,只能管理员修改 注意要点:一定要开启ssl,否则有些邮箱给你发邮件你收不到 建议:不要入坑.坏了之后没法修复,哭都没地方…...

相机单独标定的实现过程[autoware标定]、tmp文件的查看方式

安装了autoware1.13和calibration标定包&#xff0c;发现实现相机单独标定的过程较为坎坷&#xff0c;参考了一些博主的方法&#xff0c;发现下面的过程更加适合自己&#xff0c;做个笔记。 1安装标定箱&#xff08;与calibration标定包的安装并不冲突&#xff09; 标定工具箱…...

4.10.1、IP 多播技术的相关基本概念

多播&#xff08;Multicast&#xff0c;也称为组播&#xff09;是一种实现 “一对多” 通信的技术&#xff0c;与传统单播“一对一”通信相比&#xff0c;多播可以极大地节省网络资源。 在因特网上进行的多播&#xff0c;称为 IP 多播。 1、单播 & 多播 如下所示&#xf…...

PIGOSS BSM监控国产数据库Oscar

前言神通数据库(原OSCAR数据库&#xff09;是天津神舟通用数据技术有限公司&#xff08;简称“神舟通用公司”&#xff09;拥有自主知识产权的企业级、大型通用关系型数据库管理系统。PIGOSS BSM作为网利友联科技完全自主研发的纯国产基础 IT 架构运行状态监测平台软件&#xf…...