【优选算法篇】分治乾坤,万物归一:在重组中窥见无声的秩序
文章目录
- 分治专题(二):归并排序的核心思想与进阶应用
- 前言、
- 第二章:归并排序的应用与延展
- 2.1 归并排序(medium)
- 解法(归并排序)
- C++ 代码实现
- 易错点提示
- 时间复杂度和空间复杂度
 
- 2.2 数组中的逆序对(hard)
- 解法(利用归并排序的过程 — 分治)
- 核心步骤与实现细节
- C++ 代码实现
- 易错点提示
- 时间复杂度和空间复杂度
 
- 2.3 计算右侧小于当前元素的个数(hard)
- 解法(利用归并排序的过程 — 分治)
- 核心步骤
- C++ 代码实现
- 易错点提示
- 时间复杂度和空间复杂度
 
- 2.4 翻转对(hard)
- 解法(归并排序 — 分治)
- 核心步骤与实现细节
- C++ 代码实现
- 易错点提示
- 时间复杂度和空间复杂度
- 优化点
 
 
- 写在最后
 
分治专题(二):归并排序的核心思想与进阶应用
🚀 欢迎讨论:如果您对内容有任何疑问或见解,欢迎在评论区留言。
👍 点赞、收藏与分享:如果觉得这篇文章对您有帮助,请点赞、收藏并分享给更多朋友。
💡 分享给更多人:一起学习分治策略,掌握归并排序的精髓!
前言、
上篇:【优选算法篇】化繁为简,见素抱朴:从乱象中重构秩序的艺术
归并排序是经典的分治法应用,其核心在于“分而治之”的思想。通过不断划分,将一个复杂问题逐步拆解成若干规模更小的子问题,以递归方式求解,再将解合并,从而解决初始问题。本文将围绕归并排序的基本原理,结合排序数组的题目,深入剖析归并排序在分治中的实际应用。
第二章:归并排序的应用与延展
2.1 归并排序(medium)
题目链接:912. 排序数组
题目描述:
给定一个整数数组 nums,请将该数组按升序排列。
示例 1:
- 输入:nums = [5,2,3,1]
- 输出:[1,2,3,5]
示例 2:
- 输入:nums = [5,1,1,2,0,0]
- 输出:[0,0,1,1,2,5]
解法(归并排序)
算法思路:
归并排序的过程充分体现了“分而治之”的思想,基本步骤分为以下两部分:
-  分:将数组一分为二,递归地继续分割,直到每个子数组的长度为 1,确保所有分块都已排序。 
-  治:将两个已排序的子数组合并成一个有序数组,最终返回整个数组的有序结果。 
具体步骤:
- 使用中间点将数组分成 [left, mid]和[mid + 1, right]两部分。
- 递归对左右区间进行排序。
- 在排序好的左右子数组中,使用双指针将较小的元素依次合并到临时数组 tmp中。
- 合并完成后,将 tmp数组的内容拷贝回原数组。
C++ 代码实现
class Solution {vector<int> tmp; // 临时数组用于存储合并结果
public:vector<int> sortArray(vector<int>& nums) {tmp.resize(nums.size());mergeSort(nums, 0, nums.size() - 1);return nums;}// 归并排序void mergeSort(vector<int>& nums, int left, int right) {if (left >= right) return; // 递归终止条件// 1. 分区int mid = (left + right) / 2;mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);// 2. 合并两个已排序数组int cur1 = left, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= right) {tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];}while (cur1 <= mid) tmp[i++] = nums[cur1++]; // 左边剩余部分while (cur2 <= right) tmp[i++] = nums[cur2++]; // 右边剩余部分// 3. 拷贝回原数组for (int j = 0; j < i; ++j) {nums[left + j] = tmp[j];}}
};
易错点提示
-  递归终止条件: - 在 mergeSort函数中,left >= right时停止递归,以防止无穷递归导致的栈溢出。
 
- 在 
-  合并逻辑: - cur1和- cur2分别指向左、右子数组的起始位置,使用- tmp存储合并结果。确保在所有元素都合并后,将- tmp拷贝回- nums中。
 
-  临时数组的使用: - 临时数组 tmp存储每一轮合并结果,以确保排序结果被完整保留到下一轮递归。
 
- 临时数组 
时间复杂度和空间复杂度
- 时间复杂度:O(n log n)。每轮合并的时间复杂度为O(n),分区深度为log n。
- 空间复杂度:O(n),临时数组tmp占用额外空间。
2.2 数组中的逆序对(hard)
题目链接:剑指 Offer 51. 数组中的逆序对
题目描述:
在一个数组中的两个数字,如果前面的一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
- 输入:[7,5,6,4]
- 输出:5
解法(利用归并排序的过程 — 分治)
算法思路:
归并排序求逆序数是经典的方法。在归并排序的合并过程中,我们可以顺便统计出逆序对的数量,这样可以避免暴力统计的 O(n^2) 时间复杂度。
逆序对的产生方式可分为三种情况:
- 左数组中的元素逆序。
- 右数组中的元素逆序。
- 左数组和右数组中的元素形成逆序。
归并排序过程可以分为两个步骤:
- 分:将数组一分为二,不断划分,直到每个子数组长度为1。
- 治:将左右子数组合并成一个有序数组的过程中,统计逆序对的数量。
核心步骤与实现细节
-  为什么可以利用归并排序求逆序对? 因为在归并排序的过程中,我们通过分治的方法将数组拆分成左右两部分。每部分内部的逆序对可以分别在排序的过程中统计。最终在归并左右数组时,可以统计那些横跨左右数组的逆序对,这三者相加即为总的逆序对数。 
-  核心问题:如何在合并两个有序数组的过程中统计逆序对的数量? 在合并左右两个已排序的数组时,可以高效统计出横跨两个数组的逆序对数。考虑以下例子: 假设有两个有序序列 left = [5, 7, 9]和right = [4, 5, 8]。
 目标是计算在合并的过程中left中的元素大于right中的元素的情况数量。
-  合并并统计逆序对的过程: 定义指针 cur1遍历left数组,cur2遍历right数组。
 定义ret记录逆序对的数量,help数组临时存储排序的结果。-  第一轮: - 比较 left[cur1]和right[cur2],如果left[cur1] > right[cur2],则意味着从cur1到末尾的元素都大于right[cur2](因为left是有序的)。我们可以确定逆序对数量并将结果累加到ret。
- 若 left[cur1] <= right[cur2],则将left[cur1]添加到help数组中。
 
- 比较 
-  处理剩余元素:若一侧数组元素遍历完,剩余元素直接添加到 help数组即可,不会再产生新的逆序对。
 
-  
C++ 代码实现
class Solution {int tmp[50010]; // 临时数组
public:int reversePairs(vector<int>& nums) {return mergeSort(nums, 0, nums.size() - 1);}int mergeSort(vector<int>& nums, int left, int right) {if (left >= right) return 0; // 递归终止条件int ret = 0;// 1. 找中间点,将数组分成两部分int mid = (left + right) >> 1;// 2. 左、右部分的逆序对数量ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);// 3. 合并并统计逆序对数量int cur1 = left, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= right) {if (nums[cur1] <= nums[cur2]) {tmp[i++] = nums[cur1++]; // 左侧元素较小,无逆序对} else {ret += mid - cur1 + 1; // 右侧元素小,统计逆序对tmp[i++] = nums[cur2++];}}// 4. 处理剩余元素while (cur1 <= mid) tmp[i++] = nums[cur1++];while (cur2 <= right) tmp[i++] = nums[cur2++];// 5. 将 tmp 数组内容拷贝回原数组for (int j = left; j <= right; j++) {nums[j] = tmp[j - left];}return ret;}
};
易错点提示
-  递归终止条件: - 当 left >= right时,表示已将数组分割到单个元素,递归停止返回0。
 
- 当 
-  统计逆序对: - 当 nums[cur1] > nums[cur2]时,说明left[cur1]及后面的所有元素都大于right[cur2],则这些元素与right[cur2]构成逆序对。统计并更新ret的值。
 
- 当 
-  处理剩余元素: - 若左数组或右数组有剩余,则直接将剩余部分加入到 tmp,因为剩余部分不会形成逆序对。
 
- 若左数组或右数组有剩余,则直接将剩余部分加入到 
-  拷贝回原数组: - 最后将 tmp的排序结果拷贝回nums,确保在递归的上一级合并时数组依旧有序。
 
- 最后将 
时间复杂度和空间复杂度
- 时间复杂度:O(n log n)。归并排序的分治递归实现使得时间复杂度达到O(n log n)。
- 空间复杂度:O(n),需要额外的数组存储合并结果。
2.3 计算右侧小于当前元素的个数(hard)
题目链接:315. 计算右侧小于当前元素的个数
题目描述:
给你一个整数数组 nums ,按要求返回一个新数组 counts。
 数组 counts 有如下性质:counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例 1:
- 输入:nums = [5,2,6,1]
- 输出:[2,1,1,0]
解释:
- 5 的右侧有 2 个更小的元素(2 和 1)。
- 2 的右侧有 1 个更小的元素(1)。
- 6 的右侧有 1 个更小的元素(1)。
- 1 的右侧有 0 个更小的元素。
解法(利用归并排序的过程 — 分治)
算法思路:
本题和求数组中的逆序对非常类似,但与逆序对的统计不同之处在于:
- 需要返回一个数组 counts,记录每个元素右侧更小的元素数量,而不是单一的逆序对总数。
- 归并排序过程中,除了排序,还需要记录排序过程中元素的索引位置的变化。
核心步骤
-  辅助数组和索引绑定: - 定义一个 index数组,用于记录元素对应的原始索引,确保元素与下标绑定在一起。
- ret数组存储每个元素右侧更小元素的数量。
 
- 定义一个 
-  递归排序: - 递归地对数组进行分治排序,并统计左右子数组中每个元素右侧更小的元素数量。
 
-  合并排序并统计逆序对: - 当合并两个有序数组时,如果发现左侧的某个元素大于右侧的当前元素,则说明该左侧元素右侧的所有元素都小于它。将这些逆序对数量累加到对应的 ret索引上。
- 同时,完成左右子数组的归并操作。
 
- 当合并两个有序数组时,如果发现左侧的某个元素大于右侧的当前元素,则说明该左侧元素右侧的所有元素都小于它。将这些逆序对数量累加到对应的 
C++ 代码实现
class Solution {vector<int> ret;        // 结果数组vector<int> index;      // 记录元素的原始下标int tmpNums[100010];    // 临时数组用于排序int tmpIndex[100010];   // 临时数组用于记录索引排序public:vector<int> countSmaller(vector<int>& nums) {int n = nums.size();ret.resize(n, 0);       // 初始化结果数组为0index.resize(n);        // 初始化下标数组for (int i = 0; i < n; i++) {index[i] = i;       // 初始时,索引与数组位置一一对应}mergeSort(nums, 0, n - 1);return ret;}void mergeSort(vector<int>& nums, int left, int right) {if (left >= right) return;// 1. 分区int mid = (left + right) / 2;// 2. 递归处理左右子区间mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);// 3. 合并并统计逆序对int cur1 = left, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= right) {if (nums[cur1] <= nums[cur2]) {tmpNums[i] = nums[cur2];tmpIndex[i++] = index[cur2++];} else {// 当前左侧元素大于右侧元素,统计逆序对ret[index[cur1]] += right - cur2 + 1;tmpNums[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}}// 处理剩余元素while (cur1 <= mid) {tmpNums[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}while (cur2 <= right) {tmpNums[i] = nums[cur2];tmpIndex[i++] = index[cur2++];}// 拷贝回原数组for (int j = left; j <= right; j++) {nums[j] = tmpNums[j - left];index[j] = tmpIndex[j - left];}}
};
易错点提示
-  确保索引正确绑定: - 在每次合并的过程中,索引数组 index也需要同步排序,以保证结果数组ret的统计准确。
 
- 在每次合并的过程中,索引数组 
-  统计逆序对的逻辑: - 当 nums[cur1] > nums[cur2]时,说明从cur2到right的所有元素都小于nums[cur1],需要将这些数量累加到ret[index[cur1]]。
 
- 当 
-  递归终止条件: - 当 left >= right时停止递归,避免无效操作。
 
- 当 
时间复杂度和空间复杂度
- 时间复杂度:O(n log n)。分治递归的时间复杂度是O(log n),每次合并的时间复杂度是O(n)。
- 空间复杂度:O(n)。需要额外的临时数组tmpNums和tmpIndex进行合并排序。
2.4 翻转对(hard)
题目链接:493. 翻转对
题目描述:
给定一个数组 nums,如果 i < j 且 nums[i] > 2 * nums[j],我们就将 (i, j) 称作一个重要翻转对。
 你需要返回给定数组中的重要翻转对的数量。
示例 1:
- 输入:nums = [1,3,2,3,1]
- 输出:2
解法(归并排序 — 分治)
算法思路:
翻转对的统计与逆序对非常类似,都可以利用 归并排序 的思想,将问题分解为三部分:
- 左数组中的翻转对数量。
- 右数组中的翻转对数量。
- 左右数组合并时的翻转对数量。
由于翻转对要求的是 nums[i] > 2 * nums[j],直接在合并排序时统计会比较困难,因此:
- 我们在归并排序前,提前统计 左右两部分数组中满足条件的翻转对数量。
- 在统计完成后,再进行归并排序。
核心步骤与实现细节
-  统计翻转对数量: - 使用两个指针 cur1和cur2,分别遍历左、右子数组。
- 对于每个左数组元素 nums[cur1],找到右数组中第一个不满足nums[cur1] > 2 * nums[cur2]的位置cur2。
- 此时,cur2 - mid - 1即为当前cur1的翻转对数量,累加到结果中。
 
- 使用两个指针 
-  合并两个有序数组: - 合并时,按照归并排序的逻辑,将两个子数组排序,并写入临时数组。
 
-  递归分治: - 每次划分数组为 [left, mid]和[mid + 1, right],递归统计翻转对数量并排序。
 
- 每次划分数组为 
C++ 代码实现
class Solution {int tmp[50010]; // 临时数组用于合并排序
public:int reversePairs(vector<int>& nums) {return mergeSort(nums, 0, nums.size() - 1);}int mergeSort(vector<int>& nums, int left, int right) {if (left >= right) return 0;int ret = 0;// 1. 分区int mid = (left + right) / 2;// 2. 递归计算左右区间的翻转对数量ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);// 3. 统计翻转对数量int cur1 = left, cur2 = mid + 1;while (cur1 <= mid) {while (cur2 <= right && nums[cur2] * 2 < nums[cur1]) {cur2++;}ret += cur2 - mid - 1;cur1++;}// 4. 合并两个有序数组cur1 = left, cur2 = mid + 1;int i = left;while (cur1 <= mid && cur2 <= right) {tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];}while (cur1 <= mid) tmp[i++] = nums[cur1++];while (cur2 <= right) tmp[i++] = nums[cur2++];for (int j = left; j <= right; j++) {nums[j] = tmp[j];}return ret;}
};
易错点提示
-  统计翻转对的逻辑: - 必须确保 cur2指针只向右移动,不会回退,避免重复计算。
- 注意翻转对的判断条件 nums[cur1] > 2 * nums[cur2],需要考虑到浮点运算可能导致的精度问题,因此2 * nums[cur2]可能需要写成nums[cur1] > 2LL * nums[cur2]。
 
- 必须确保 
-  归并排序的逻辑: - 合并时,确保临时数组和原数组的元素同步,避免在递归的上一层出错。
 
-  边界条件: - 单元素或空数组的递归终止条件是 left >= right。
 
- 单元素或空数组的递归终止条件是 
时间复杂度和空间复杂度
-  时间复杂度: O(n log n)。- 每次递归的深度是 log n,每层递归需要合并排序和统计翻转对,复杂度为O(n)。
 
- 每次递归的深度是 
-  空间复杂度: O(n)。- 使用了额外的临时数组 tmp进行排序。
 
- 使用了额外的临时数组 
优化点
- 使用更大的临时数组(避免频繁分配内存)。
- 在统计翻转对时,提前检查是否有可能的翻转对,减少无意义的遍历。
通过以上方法,翻转对的数量可以在 O(n log n) 的时间复杂度内高效统计。
写在最后
在本次归并排序专题中,我们以经典的分治思想为核心,逐层剖析了归并排序的精髓及其在算法中的高级应用。从数组排序到统计逆序对,再到复杂的翻转对和右侧更小元素计数,归并排序不仅是解决基础问题的利器,更是攻克高阶难题的关键。我们以逐步深入的方式,从基础实现到优化分析,贯穿了分治思想的深度与广度。
分治法的核心在于“分而治之”,通过不断将大问题拆解为小问题,并利用递归与合并的方式重新组合结果。在归并排序中,借助临时数组和指针操作,我们能够高效完成排序,并在此过程中完成复杂的统计任务。它不仅展现了算法的美学,更体现了计算机科学中化繁为简的哲学。
通过归并排序的深入学习,我们不仅掌握了分治策略的实现,更体验了算法优化的艺术。期待这篇文章能为读者打开分治法的大门,让你在算法学习中游刃有余!
以上就是关于【优选算法篇】分治乾坤,万物归一:在重组中窥见无声的秩序的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

相关文章:
 
【优选算法篇】分治乾坤,万物归一:在重组中窥见无声的秩序
文章目录 分治专题(二):归并排序的核心思想与进阶应用前言、第二章:归并排序的应用与延展2.1 归并排序(medium)解法(归并排序)C 代码实现易错点提示时间复杂度和空间复杂度 2.2 数组…...
 
C++:探索AVL树旋转的奥秘
文章目录 前言 AVL树为什么要旋转?一、插入一个值的大概过程1. 插入一个值的大致过程2. 平衡因子更新原则3. 旋转处理的目的 二、左单旋1. 左单旋旋转方式总处理图2. 左单旋具体会遇到的情况3. 左单旋代码总结 三、右单旋1. 右单旋旋转方式总处理图2. 右单旋具体会遇…...
 
2. Django中的URL调度器 (自定义路径转换器)
在 Django 中,URL 路由通常使用路径转换器(path converters)来匹配和捕获 URL 中的特定模式,例如整数、字符串或 slug 等。默认情况下,Django 提供了一些内置的路径转换器,如 <int>、<str>、&l…...
深度学习:神经网络中线性层的使用
深度学习:神经网络中线性层的使用 在神经网络中,线性层(也称为全连接层或密集层)是基础组件之一,用于执行输入数据的线性变换。通过这种变换,线性层可以重新组合输入数据的特征,并将其映射到新…...
【刷题】算法设计题+程序设计题【2】2019-2024
11.202019年真题*2BST二叉排序树分裂、双向冒泡排序 2019 真题 【2019 1】编写算法,将一棵二叉排序树 分解成两棵二叉排序树 t1和t2,使得t1中的所有结点关键字的值都小于x,t2中所有结点关键字都大于x。 typedef struct BSTNode{int data;str…...
搭建es环境
centos7搭建elasticsearch环境 首先考虑使用 Docker 来安装 Elasticsearch、Kibana 和 Logstash。在安装过程中,可能会遇到一些问题,但通过适当的方法可以解决。 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.14.3 首先创建一个网络&a…...
阿里云和七牛云对象存储区别和实现
七牛云对象存储操作(QiniuUtil) 配置:使用 com.qiniu.storage.Configuration 类来配置上传设置,如指定区域(Region)和分片上传版本。上传管理器:通过 UploadManager 类来处理文件上传。认证&am…...
 
uniapp微信小程序接入airkiss插件进行WIFI配网
本文可参考uniapp小程序插件 一.申请插件 微信公众平台设置页链接:微信公众平台 登录您的小程序微信公众平台,进入设置页,在第三方设置->插件管理->添加插件中申请AiThinkerAirkissforWXMini插件,申请的插件appId为【wx6…...
 
03 —— Webpack 自动生成 html 文件
HtmlWebpackPlugin | webpack 中文文档 | webpack中文文档 | webpack中文网 安装 npm install --save-dev html-webpack-plugin 下载html-webpack-plugin本地软件包 npm i html-webpack-plugin --save-dev 配置webpack.config.js让webpack拥有插件功能 const HtmlWebpack…...
 
Python毕业设计选题:基于python的豆瓣电影数据分析可视化系统-flask+spider
开发语言:Python框架:flaskPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 系统首页 个人中心 管理员登录界面 管理员功能界面 电影管理 用户管理 系统管理 摘要…...
抽象类能使用final修饰吗?
不能。 在java中,抽象类不能使用final修饰。原因是final修饰符用于类不能被继承,而抽象类的主要用途就是被继承以提供基础实现或定义抽象方法供子类实现。这两个互相矛盾,因此不能同时使用。 具体解释 abstract修饰符:用于定义一个抽象类&…...
C语言内存:我家大门常打开
C语言本着自由开放的理念,并不禁止程序访问非法内存。 什么是非法内存?就是那本不是你家的地,你却硬跑过去种庄稼。 或者,你在澡堂子里拿着自己的钥匙去捅别人的柜。 这种行为当然后果难料。 可能你捅了半天,火花冒…...
 
路由协议——iBGP与EBGP
一、适用场景 1、企业需要连接总部与分部,但总部与分部运行着不同的路由协议,总部到分部有自建的专线,端到端的设备支持BGP路由协议。 2、网络运营商,如电信、联通、移动等,各区域的ip路由表庞大,若要完成…...
 
【Linux】基础02
Linux编译和调试 VI编辑文件 vi : 进入文件编辑 是命令行模式 i :从光标处进入插入模式 dd : 删除光标所在行 n dd 删除指定行数 Esc : 退出插入模式 : 冒号进入末行模式 :wq : 保存退出 :q : 未修改文件可以退出 :q! …...
Elasticsearch面试内容整理-安全与权限管理
在 Elasticsearch 中,安全与权限管理至关重要,特别是当系统处理敏感数据时。Elasticsearch 提供了一套全面的安全机制来确保数据的机密性、完整性和可用性。以下是 Elasticsearch 安全与权限管理的详细介绍。 安全组件概述 Elasticsearch 的安全功能由 Elastic Stack 提供的一…...
 
【数据分享】中国汽车工业年鉴(1986-2023)
本年鉴是由工业和信息化部指导,中国汽车技术研究中心有限公司与中国汽车工业协会联合主办。《年鉴》是全面、客观记载中国汽车工业发展与改革历程的重要文献,内容涵盖汽车产业政策、标准、企业、市场以及全国各省市汽车工业发展情况,并调查汇…...
 
el-cascader 使用笔记
1.效果 2.官网 https://element.eleme.cn/#/zh-CN/component/cascader 3.动态加载(官网) <el-cascader :props"props"></el-cascader><script>let id 0;export default {data() {return {props: {lazy: true,lazyLoad (…...
代替Spinnaker 的 POINTGREY工业级相机 FLIR相机 Python编程案例
SpinnakerSDK_FULL_4.0.0.116_x64 是一个用于FLIR相机的SDK,主要用于图像采集和处理。Spinnaker SDK主要提供C接口,无法直接应用在python环境。本文则基于Pycharm2019python3.7的环境下,调用opencv,EasySpin,PySpin,的库实现POINTGREY工业级相…...
 
网络篇12 | SSH2协议应用,禁SFTP子模式实现文件传输
网络篇12 | SSH2的应用 解决的业务问题协议选定SSH2(Secure Shell 2,目前基本用这个)SSH1(Secure Shell 1)Telnet 代码实现落地方案1:ganymed-ssh2maven坐标关键源代码技术效果验证连接高版本OpenSSH报错分…...
 
MetaGPT实现多动作Agent
异步编程学习链接 智能体 LLM观察思考行动记忆 多智能体 智能体环境SOP评审路由订阅经济 教程地址 多动作的agent的本质是react,这包括了think(考虑接下来该采取啥动作)act(采取行动) 在MetaGPT的examples/write_…...
 
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
 
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
 
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
 
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
