算法分析与设计之分治算法
文章目录
- 前言
- 一、分治算法divide and conquer
- 1.1 分治定义
- 1.2 分治法的复杂性分析:递归方程
- 1.2.1 主定理
- 1.2.2 递归树法
- 1.2.3 迭代法
- 二、典型例题
- 2.1 Mergesort
- 2.2 Counting Inversions
- 2.3 棋盘覆盖
- 2.4 最大和数组
- 2.5 Closest Pair of Points
- 2.6 Karatsuba算法(大整数的乘法)
- 2.7 Matrix Multiplication 矩阵乘法
- 结束语
- 💂 个人主页:风间琉璃
- 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
- 💬 如果文章对你有
帮助
、欢迎关注
、点赞
、收藏(一键三连)
和订阅专栏
哦
前言
提示:这里可以添加本文要记录的大概内容:
预览:
一、分治算法divide and conquer
1.1 分治定义
分治算法(divide and conquer)的核心思想:分而治之 ,即将原问题划分成 k
个规模较小,并且结构与原问题相似的独立子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。
分治所能解决的问题一般具有以下几个特征:
-
该问题的规模缩小到一定的程度可以直接求解
绝大多数问题都可以满足该特征,因为问题的计算复杂性一般是随着问题规模的增加而增加;
-
该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质
这个特征是应用分治算法的前提,也是大多数问题可以满足的,此特征反映了 递归思想 的引用;
-
利用该问题分解出的子问题的解可以合并为该问题的解
能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心算法或动态规划法;
-
该问题所分解出的各个子问题是相互独立的,即**子问题之间不包含公共的子问题 **
第四条特征涉及到 分治的效率,如果各子问题是不独立的,则分治要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治,但 一般用动态规划较好。
分治算法是一种通过递归的方式将问题分解为多个子问题,然后分别解决这些子问题,最后合并子问题的解以得到原问题解的方法。分治算法一般都比较适合用递归来实现,通常由以下三个步骤组成:
-
分解(Divide):将原问题划分为若干个规模较小但结构与原问题相同的子问题。
-
解决(Conquer):若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题。
-
合并(Combine):将子问题的解组合成原问题的解,自底向上逐步求出原来问题的解。
它的一般的算法设计模式
如下:
Divide-and-Conquer(P)
{// 如果问题 P 的规模小于或等于阈值 n0(即问题足够小),则直接用 ADHOC 算法解决// ADHOC(P) 是用于处理小规模问题的基本算法if(|P| ≤ n0) ADHOC(P); // 将问题 P 拆分为 k 个子问题 P1, P2, ..., Pk. 每个子问题的规模比原问题更小,通常按照某种划分规则进行分解divide P into smaller subinstances P1,P2,...,Pk;//遍历所有 k 个子问题,逐个处理for(i=1;i<k;i++){yi ← Divide-and-Conquer(Pi) //递归调用 Divide-and-Conquer(Pi) 解决子问题 Pi,将其结果存储在 yi 中}// 将所有子问题的解 y1, y2, ..., yk 通过合并算法 MERGE 组合为原问题 P 的解return MERGE(y1, y2, ..., yk);
}
|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC§是用于处理小规模问题的直接算法。这是分治算法的终止条件,也是递归的基本情形。当问题规模小到不需要进一步分解时,即当P的规模不超过n0,可以用算法ADHOC§求解。
Divide-and-Conquer(Pi)递归调用表示继续将子问题划分为更小的子问题,直到这些子问题的规模小于或等于阈值 n0时,使用 ADHOC 方法直接解决。这是分治法中的“治”的部分。MERGE(y1,y2,…,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,…,Pk的相应的解y1,y2,…,yk合并为P的解。
1.2 分治法的复杂性分析:递归方程
分治算法的复杂度通常由递归关系式来表示。**假设每次分解后生成 a个子问题,每个子问题的规模是原问题的 1 / b 1/b 1/b,即每个子问题的大小为 ∣ P ∣ / b ∣P∣/b ∣P∣/b,**而合并过程的复杂度为 f ( n ) f(n) f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有递归关系式为:
T ( n ) = a T ( n / b ) + f ( n ) T(n)=aT(n/b)+f(n) T(n)=aT(n/b)+f(n)
这个关系式的解可以用主定理(Master Theorem)来分析,主定理根据 a、b 和f(n)的值给出了不同情况下的复杂度。但是并不是所有的递归方程都可以使用主定理求解,对于合并代价是非多项式、分解不符合 a T ( n / b ) aT(n/b) aT(n/b)的形式主定理不适应。此时可以考虑使用迭代法和递归树法求解。
1.2.1 主定理
对于如下的递归表达式可以通过比较 f ( n ) f(n) f(n)和 n l o g b a n^{log_ba} nlogba的大小进行判定,谁大取谁,相等乘logn,判断顺序推荐1->3->2。

对于第三种情况f(n)大,但满足前一个条件还需要满足正则条件 a f ( n / b ) ≤ c f ( n ) , c < 1 af(n/b) \leq cf(n), c<1 af(n/b)≤cf(n),c<1,需要找到一个满足条件c这个定理才适用。
- T ( n ) = 9 T ( n / 3 ) + n T(n) = 9T(n/3) + n T(n)=9T(n/3)+n
a = 9 , b = 3 , n l o g 3 9 = n 2 ≥ f ( n ) = n ; ( n 2 大,直接取 n 2 ) 通过主定理得出: T ( n ) = O ( n 2 ) a = 9,b=3,n^{{log_{3}9}} = n^2 \geq f(n)=n; \\ (n^2大,直接取n^2)通过主定理得出:T(n) = O(n^2) a=9,b=3,nlog39=n2≥f(n)=n;(n2大,直接取n2)通过主定理得出:T(n)=O(n2)
给定递归式: T ( n ) = 2 T ( n 2 ) + f ( n ) , 其中 f ( n ) = n log n T(n) = 2T\left(\frac{n}{2}\right) + f(n), \quad \text{其中 } f(n) = n \log n T(n)=2T(2n)+f(n),其中 f(n)=nlogn。
log b a = log 2 2 = 1 \log_b a = \log_2 2 = 1 logba=log22=1,而 f ( n ) = n log n f(n) = n \log n f(n)=nlogn,大于 n log 2 2 = n n^{\log_2 2} = n nlog22=n。是否满足第三种情况呢? f ( n ) / n l o g b a = n l o g b a / n = l o g n < n ϵ f(n)/ n^{log_ba}=nlog_ba/n=logn < n^ϵ f(n)/nlogba=nlogba/n=logn<nϵ,f(n)大于 ,但不是多项式地大于,则不能用主方法求解该递归式。这里要求 f ( n ) / n l o g b a = n l o g b a / n = n ϵ f(n)/ n^{log_ba}=nlog_ba/n= n^ϵ f(n)/nlogba=nlogba/n=nϵ,因此只能使用迭代法或者递归树。
不适用主定理的情况:
子问题数量 a不是常数,比如动态变化。
T ( n ) = n T ( n 2 ) + n 2 T(n) = nT\left(\frac{n}{2}\right) + n^2 T(n)=nT(2n)+n2,子问题数量 a=n,不是常数。无法直接用主定理,因为a是输入规模的函数。
子问题数量a小于1
T ( n ) = 1 2 T ( n 2 ) + n 2 T(n) = \frac{1}{2}T\left(\frac{n}{2}\right) + n^2 T(n)=21T(2n)+n2,子问题数量 a = 1/2,理论上少于 1,这种形式不符合递归定义(实际问题通常不这样设置)。若假设 a 的实际意义是问题的分布比 1 小一半,则递归展开的分量逐渐减少,主定理仍不适用。
子问题的划分和合并代价不能简化为单一的多项式。
T ( n ) = 2 T ( n 2 ) + n log n T(n) = 2T\left(\frac{n}{2}\right) + n \log n T(n)=2T(2n)+nlogn。
1.2.2 递归树法
T ( n ) = 2 T ( n / 2 ) + n 2 T(n) = 2T(n/2) + n^2 T(n)=2T(n/2)+n2使用递归树。

所以总的时间复杂度为
T ( n ) = n 2 2 0 + 2 ( n 2 4 ) + 4 ( n 2 16 ) + ⋯ + 2 log 2 n ( n 2 2 log 2 n + log 2 n ) ( log 2 n 层 ) = n 2 ( 1 + 1 2 + 1 2 2 + ⋯ + 1 2 log 2 n ) = n 2 ( 1 − ( 1 2 ) log 2 n + 1 1 − 1 2 ) = 2 n 2 − n \begin{equation} \begin{aligned} T(n) =& \frac {n^2} {2^0} + 2( \frac {n^2} {4} ) +4( \frac {n^2} {16} ) + \dots + 2^{\log_2 n}( \frac {n^2} {2^{\log_2 n+\log_2 n}} ) \ (\log_2 n \text{ 层}) \\ =& n^2( 1+ \frac {1} {2} + \frac {1} {2^2} + \dots + \frac {1} {2^{\log_2 n}}) \\ =& n^2(\frac {1- (\frac{1}{2})^{\log_2 n+1}} {1- \frac{1}{2}}) \\ =& 2n^2 - n \end{aligned} \end{equation} T(n)====20n2+2(4n2)+4(16n2)+⋯+2log2n(2log2n+log2nn2) (log2n 层)n2(1+21+221+⋯+2log2n1)n2(1−211−(21)log2n+1)2n2−n
所以 T ( n ) = O ( n 2 ) T(n) = O(n^2) T(n)=O(n2)。
结论:
递归深度是由每次递归缩小问题规模的因子b决定的,因此递归的深度为$ \log_b n$。
即使每次递归会产生多个子问题(比如 3 个),这只会影响递归树的宽度,而深度不变。
补充:

对于一个无穷等比级数:$S = a + aq + aq^2 + aq^3 + \cdots 。其中: 。其中: 。其中:a 是首项, 是首项, 是首项,q$是公比,且 ( |q| < 1 )。
该无穷等比级数的和 ( S ) 为:
S = a 1 − q , 当 ∣ q ∣ < 1 S = \frac{a}{1 - q}, \quad \text{当 } |q| < 1 S=1−qa,当 ∣q∣<1
1.2.3 迭代法
迭代法(又称递归展开法)是一种通过逐步展开递归方程,分析每一层递归调用的代价来求解递归关系的方法。它的核心思想是通过展开递归关系,观察每一层的代价,直至达到递归的基准条件,从而得出递归的总体代价(时间复杂度)。
求解 T ( n ) = 2 T ( n 2 ) + n log n T(n) = 2T\left(\frac{n}{2}\right) + n \log n T(n)=2T(2n)+nlogn
用递推展开法:
-
展开递归:
T ( n ) = 2 ( 2 T ( n 4 ) + n 2 log n 2 ) + n log n T(n) = 2 \left( 2T\left(\frac{n}{4}\right) + \frac{n}{2} \log \frac{n}{2} \right) + n \log n T(n)=2(2T(4n)+2nlog2n)+nlognT ( n ) = 4 T ( n 4 ) + n log n 2 + n log n T(n) = 4T\left(\frac{n}{4}\right) + n \log \frac{n}{2} + n \log n T(n)=4T(4n)+nlog2n+nlogn
-
继续递归 k 次,得:
T ( n ) = 2 k T ( n 2 k ) + ∑ i = 0 k − 1 2 i ⋅ n 2 i ⋅ log n 2 i T(n) = 2^k T\left(\frac{n}{2^k}\right) + \sum_{i=0}^{k-1} 2^i \cdot \frac{n}{2^i} \cdot \log \frac{n}{2^i} T(n)=2kT(2kn)+i=0∑k−12i⋅2in⋅log2in -
当 k = log n k = \log n k=logn时,问题规模为 1,得 T(1)=O(1): T ( n ) = O ( n log 2 n ) T(n) = O(n \log^2 n) T(n)=O(nlog2n)。
每次递归将问题规模缩小一半,因此递归树的深度(层数)是 log 2 n \log_2 n log2n,即从 n缩小到 1 需要 log b n \log_ bn logbn次递归,递归深度是由每次递归缩小问题规模的因子b决定的,
二、典型例题
2.1 Mergesort
排序问题:给定n个元素,要求将它们重新排列为升序或降序的顺序。
归并排序是一种经典的 分治法 排序算法,以下是其具体步骤:
- 分解(Divide):将数组划分为两个相等或接近相等的子数组。
- 递归排序(Conquer):对每个子数组递归调用归并排序,直到子数组的大小为 1(自然是有序的)。
- 合并(Combine):将两个有序的子数组 合并 为一个有序数组。

如何高效合并两个有序列表,使用线性时间完成。给定两个有序数组A和B,需要合并它们为C,保持C的元素有序。
假设:A长度为m,B长度为n。![]()
合并算法过程:
- 使用两个指针i和j,分别指向A和B的起始位置。
- 比较A[i]和B[j的值,将较小的元素复制到临时数组C中,并移动相应指针。
- 当一个数组的指针到达末尾时,将另一个数组的剩余元素直接复制到C 中。
每次比较后指针移动,因此比较次数最多为 m+n,整体复杂度为O(m+n)。
归并排序的时间复杂度T(n)表示对大小为n的输入进行归并排序所需的比较次数。它由以下递归公式定义:

将数组划分为两个大小为n/2的子数组,时间复杂度为O(1)。将两个大小为n/2的子数组合并为一个数组,所需时间为O(n)。T(n)为总的比较次数,由分解、递归、合并三部分组成。总时间复杂度为 O ( n log n ) O(n \log n) O(nlogn),无论最优、平均还是最差情况。证明方法包括展开递归、递归树分析和主定理法,明显直接套用主定理最简单。
2.2 Counting Inversions
逆序对:如果i < j,但 a i > a j a_i > a_j ai>aj,那么(i, j)是一个逆序对。如下图在序列1,3,4,2,5中3-2和4-2构成两个逆序对。

直接检查所有的逆序对,其时间复杂度为 O ( n 2 ) O(n^2) O(n2)。利用分治法求解逆序对,其时间复杂度为 O ( n log n ) O(n \log n) O(nlogn)。

-
Divide:将数组分为两部分,左半部分:从起始位置到中点;右半部分:从中点到结束。通过递归处理这两个子数组,分别计算其中的逆序对。
-
Conquer:对左右子数组递归地进行处理,统计每部分的逆序对数量。
-
Combine:合并左右子数组时,统计跨越左右部分的逆序对数量,逆序对中一个来自左边数组,另一个来自右边数组。
最终的逆序对数量等于: total inversions = left inversions + right inversions + cross inversions \text{total inversions} = \text{left inversions} + \text{right inversions} + \text{cross inversions} total inversions=left inversions+right inversions+cross inversions

前提条件:Merge-and-Count函数已经假设A和B两个子数组已经排序。
后置条件:Sort-and-Count返回一个排序后的数组,并且统计该数组中的倒排对的数量。

递归调用的深度是 O ( log n ) O(\log n) O(logn), 因为在每次递归时,我们将数组分成两个子数组,所以递归的深度是对数级别的,即 O ( log n ) O(\log n) O(logn)。每层递归的合并操作时间复杂度是 O(n),每次合并两个数组时,时间复杂度是线性的,因为每个元素都会参与合并过程。因此,整体的时间复杂度是 O ( n log n ) O(n \log n) O(nlogn)。
2.3 棋盘覆盖
残缺棋盘是一个有 2 k × 2 k ( k ≥ 1 ) 2^k×2^k (k≥1) 2k×2k(k≥1)个方格的棋盘,其中恰有一个方格残缺。下图给出k=1时各种可能的残缺棋盘,其中残缺的方格用阴影表示。

这样的棋盘称作“三格板”或者“L”型骨牌,残缺棋盘问题就是用这四种三格板覆盖更大的残缺棋盘。在覆盖中要求:
- 两个三格板不能重叠
- 三格板不能覆盖残缺方格,但必须覆盖其他所有方格
利用分治法递归地解决问题:
-
Divide棋盘分割:将棋盘划分为四个大小为 2 n − 1 × 2 n − 1 2^{n-1} \times 2^{n-1} 2n−1×2n−1的子棋盘。确定残缺点所在的子棋盘,使用对应的“L”型骨牌覆盖其余三个子棋盘的交界方格,可使另外三个子棋盘转化为独立子问题,以标记新的虚拟残缺点,保证递归的完整性。因为要保持子问题和原问题具有相同的结构。
-
Conquer: 对每个子棋盘递归应用相同的覆盖策略,直到棋盘大小为 2 × 2 2 \times 2 2×2 时(递归的基线),可以直接覆盖。
-
Combine: 当递归返回时,所有子棋盘都已覆盖,最终完成整个棋盘的覆盖。
分析:
(1)以k=2时的问题为例,用二分法进行分解得到用双线划分的四个k=1的棋盘,如下图所示。==但是分治算法适用问题特点是子问题与原问题具有相似结构。==但这四个棋盘,并不都是与原问题相似且独立的子问题。第1个子问题与原问题相似(因为残缺方格在原图的左上部,使得第一个子图中有一个残缺方格) 。而右上角、左下角和右下角三个子棋盘(也就是图中标识为2、3、4号子棋盘),并不是原问题的相似子问题,自然也就不能独立求解。

为了保证分治法能够顺利应用到所有子问题中,我们可以在每个递归步骤中进行特殊的处理:当我们划分棋盘时,可以检查每个子棋盘的残缺方格位置。对于每个子棋盘,我们需要根据其具体情况来决定如何放置 “L” 型骨牌以及如何处理残缺格子。
-
对于第一个子问题(左上角),它的处理方式与原问题一致,因此可以直接用分治法解决。
-
对于其他三个子问题,我们需要通过增加额外的覆盖步骤来保证它们的残缺格子能够被正确处理。
使用一个①号三格板(图中阴影)覆盖2、3、4号三个子棋盘的各一个方格;

把覆盖后的方格,也看作是残缺方格(称为“伪”残缺方格),这时的2、3、4号子问题就是独立且与原问题相似的子问题了。
(2)推广至k=2其它情况:

- 当残缺方格在第1个子棋盘时,用①号三格板覆盖其余三个子棋盘的交界方格
- 当残缺方格在第2个子棋盘时,则首先用②号三格板进行棋盘覆盖;
- 当残缺方格在第3个子棋盘时,则首先用③号三格板进行棋盘覆盖;
- 当残缺方格在第4个子棋盘时,则首先用④号三格板进行棋盘覆盖;
这样处理后可使另外三个子棋盘转化为独立子问题。
(3)推广至k=1,2,3,4,….:
将 2 k × 2 k 2^k×2^k 2k×2k棋盘分割为4个$2{k-1}×2{k-1} $子棋盘(a)所示。特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为将这3个无特殊方格的子棋盘转化为特殊棋盘,可用一个三格板覆盖这3个子棋盘的会合处,如 (b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。

设T(k)是覆盖一个 2 k × 2 k 2^k×2^k 2k×2k棋盘所需时间,从算法的划分策略可知,T(k)满足如下递推式:(注意这里k是在指数上)
$$
T(k)=\left{\begin{array}{l}
O(1) ;k=0\
4 T(k-1) ; k>0 \
\end{array}\right.
$$
解此递推式可得 T ( k ) = O ( 4 k ) T(k)=O(4^k) T(k)=O(4k)。由于覆盖一个 2 k × 2 k 2^k×2^k 2k×2k棋盘所需的骨牌个数为 ( 4 k − 1 ) / 3 (4^k-1)/3 (4k−1)/3,所以,该算法是一个在渐进意义下的最优算法。
2.4 最大和数组
最大子数组问题是一个经典的分治法问题(也可以动态规划),目标是在一个数组中找到具有最大和的连续子数组。例如,数组[−2, 1, −3, 4, −1, 2, 1, −5, 4]的最大和子数组是[4, −1, 2, 1],其和为6。
分治法思路:通过将数组分成两部分来递归求解问题,核心思想是将问题划分为以下三种情况:
-
最大子数组位于左半部分。
-
最大子数组位于右半部分。
-
最大子数组跨越中点。
最终通过递归计算左、右两部分的最大子数组,以及跨越中点的最大子数组,取三者中的最大值作为结果。
算法步骤:
-
Divide将数组分为两部分:左半部分 A [ l e f t … m i d ] A[left \dots mid] A[left…mid],右半部分 A [ m i d + 1 … r i g h t ] A[mid+1 \dots right] A[mid+1…right]。
-
Conquer:递归地求解左/右半部分的最大子数组。对于跨越中点的最大子数组,其最大数值必然包含中点,而且是从左边某个位置到右边某个位置的连续子数组。可以分别从中点向左和向右扩展,找到左半部分和右半部分的最大和,然后将两部分相加。比如求左边部分的最大和,从中点mid向左遍历,逐步累加元素,直到当前划分子问题的左边界。在累加过程中,记录当前的最大累加和。
-
Combine:取左半部分、右半部分以及跨越中点的最大子数组的最大值作为结果。
时间复杂度分析:分解时间每次递归将数组分为两部分,分解操作耗时 O(1)。合并时间计算跨越中点的最大子数组需要 O(n)的时间。每次递归调用两次子问题,每个子问题的规模是原问题的一半: T ( n ) = 2 T ( n 2 ) + O ( n ) T(n) = 2T\left(\frac{n}{2}\right) + O(n) T(n)=2T(2n)+O(n)。根据主定理,递归关系的时间复杂度为 O ( n log n ) O(n \log n) O(nlogn)。
2.5 Closest Pair of Points
最近点对问题 (Closest Pair of Points) 是一个经典的几何问题,给定平面上的n个点,要求找出距离最近的两个点对。
暴力解法:检查所有的点对p和q,计算它们之间的欧几里得距离,选择最小的那个。对于n个点,所有的点对数量是 ( n 2 ) = n ( n − 1 ) 2 \binom{n}{2} = \frac{n(n-1)}{2} (2n)=2n(n−1),因此总的比较次数是 O ( n 2 ) O(n^2) O(n2)。
对于二维平面上的点集,分治法是一种更高效的解决方案。
使用四象限划分的设想:将平面分成四个象限,每个象限对应一个子区域,并递归处理每个区域中的点集。这种方法的问题在于,无法保证每个象限中的点数是均匀分布的(即每个象限中有大约 n/4个点)。例如,如果所有点都位于第一象限,那么其他三个象限将为空。那么分治算法无法有效缩小问题规模,递归深度会增大,失去分治的效率。理论上的时间复杂度可能退化到接近 O ( n 2 ) O(n^2) O(n2)。
![]()
按 x-坐标排序:
-
Divide划分:按x-坐标对点集P排序。找到x-坐标的中位数,并用它画一条垂直分割线L,将点集划分为左右两部分, P L P_L PL位于L左侧或包含中位数的点; P R P_R PR:位于L右侧的点。确保左右点集大致均匀(约n/2 点)。
-
Conquer(递归解决子问题):分别递归计算 P L P_L PL和 P R P_R PR的最近点对及其最小距离:
- d L d_L dL:左半部分的最近点对距离。
- d R d_R dR:右半部分的最近点对距离。
当前最近距离 d = min ( d L , d R ) d = \min(d_L, d_R) d=min(dL,dR)。
-
Combine(合并):检查跨越 L 的点对是否有更小的距离。筛选与分割线L距离小于d的点,构成带状区域S。按y-坐标排序S中的点。逐一检查S中每个点与最多7个距离较近的点之间的距离,更新最小距离 d S d_S dS。取三者中的最小值: d = min ( d L , d R , d S ) d = \min(d_L, d_R, d_S) d=min(dL,dR,dS)。
下面考虑跨越分割线L的点对,在最近点对问题中,假设两部分点的最近距离小于 δ \delta δ。以下是优化后寻找最近点对的方法,基于几何观察和带状区域的性质。算法思想:只需检查位于距离分割线L小于 δ \delta δ的点,形成一个宽度为 2 δ 2\delta 2δ的带状区域(strip)。然后对带状区域内的点按y-坐标排序,使得检查过程可以通过较少的点对计算完成。几何观察表明,带状区域中一个点只需与排序列表中后 7 个点比较即可,无需检查所有点对,如果又暴力比较的话,时间复杂度又为 O ( n ) O(n) O(n),因此需要采取上面的措施。

-
筛选带状区域的点:从原始点集中筛选出与分割线L的水平距离小于 δ \delta δ的点,形成带状区域S: S = { p ∈ P ∣ ∣ x p − x L ∣ < δ } S = \{p \in P \mid |x_p - x_L| < \delta\} S={p∈P∣∣xp−xL∣<δ},筛选复杂度为 O(n))。
-
按y-坐标排序:对带状区域中的点按y-坐标排序。由于递归过程中原始点集已排序,因此排序复杂度为 O ( n log n ) O(n \log n) O(nlogn)。
-
检查 y-排序列表中的点对:遍历排序后的点列表,对每个点 p i p_i pi,只检查 p i p_i pi后至多 7 个点的距离。更新当前最小距离及对应点对。
令 s i s_i si 为 2 δ 2\delta 2δ-带状区域中按y-坐标排序的第i个点。
**结论:**如果 ∣ j − i ∣ > 7 |j - i| > 7 ∣j−i∣>7,则 s i s_i si和 s j s_j sj之间的距离至少是 δ \delta δ。![]()
考虑宽为 2 δ 2\delta 2δ,高为 δ \delta δ的矩形区域R,其中点 s i s_i si的y-坐标是矩形的底部。矩形的宽为 2 δ 2\delta 2δ,因为这是左右分割线两侧各延伸 δ \delta δ的范围。如果点 s j s_j sj超出矩形 R 的上边界,则它与 s i s_i si 的垂直距离必然大于 δ \delta δ,不可能是最近点对。因此,只需检查在矩形R内的点。
将 R分割为 2 × 4 = 8 2 \times 4 = 8 2×4=8个正方形区域:每个正方形的边长为 δ / 2 \delta / 2 δ/2。任意两个点在同一个正方形内的距离小于 δ / 2 \delta / \sqrt{2} δ/2。==但由于最近点对的距离至少为 δ \delta δ,==一个正方形内最多容纳 1 个点。R 中的 8 个正方形,每个正方形至多有 1 个点。因此,除了点 s i s_i si本身,矩形内最多还有 7 个其他点。

算法首先将点集按x-坐标排序,并递归地将点集分成左右两半,分别求解左右子集的最近点对距离 δ 1 \delta_1 δ1 和 δ 2 \delta_2 δ2,然后合并结果得到 δ = min ( δ 1 , δ 2 ) \delta = \min(\delta_1, \delta_2) δ=min(δ1,δ2)。在合并阶段,仅需检查位于分割线附近宽度为 2 δ 2\delta 2δ的带状区域内的点,并对按y-坐标排序的点集中每个点与其后至多7个点比较距离,更新 δ \delta δ。

时间复杂度 T ( n ) = O ( n l o g 2 n ) T(n) = O(n log^2 n) T(n)=O(nlog2n)。Shamos 1975 定理指出:使用分治法的最近点对算法,通过**在初始时对点集按 x-和 y-坐标排序,并在递归中通过归并排序维护预排序列表,**可以将总时间复杂度优化到 O ( n log n ) O(n \log n) O(nlogn)。

2.6 Karatsuba算法(大整数的乘法)
给定两个 n 位整数 x 和 y(x,y都是二进制),计算 x × y。

可以通过Karatsuba算法实现,它是一个快速的整数乘法算法,将原本的乘法问题分解为更小的子问题,从而减少计算复杂度。
分治法乘法步骤(基于二进制):
1.拆分整数
假设有两个 n 位的二进制整数x和y,将它们拆分为两部分
x = x 1 ⋅ 2 n / 2 + x 0 y = y 1 ⋅ 2 n / 2 + y 0 x=x_1 \cdot 2^{n/2}+x_0 \\ y = y_1 \cdot 2^{n/2} + y_0 x=x1⋅2n/2+x0y=y1⋅2n/2+y0
其中:
- x 1 x_1 x1 和 x 0 x_0 x0 分别是x的高位和低位(高n/2位和低n/2位)
- y 1 y_1 y1 和 y 0 y_0 y0 分别是b的高位和低位。
通过这样的拆分,可以把两个 n 位数的乘法分解为四个较小数的乘法运算,拆分可以类别于10进制,这样比较好理解。
2.子问题分解
根据二进制的乘法规则,原始的乘积 a × b a \times b a×b可以分解为以下四部分
x y = ( x 1 ⋅ 2 n / 2 + x 0 ) ( y 1 ⋅ 2 n / 2 + y 0 ) xy=(x_1 \cdot 2^{n/2}+x_0)(y_1 \cdot 2^{n/2} + y_0) xy=(x1⋅2n/2+x0)(y1⋅2n/2+y0)
展开后
x y = x 1 ⋅ y 1 ⋅ 2 n + ( x 1 ⋅ x 0 + x 0 ⋅ y 1 ) ⋅ 2 n / 2 + x 0 ⋅ y 0 xy= x_1⋅y_1⋅2^{n}+(x_1⋅x_0+x_0⋅y_1)⋅2^{n/2}+x_0⋅y_0 xy=x1⋅y1⋅2n+(x1⋅x0+x0⋅y1)⋅2n/2+x0⋅y0
这个公式中有四个乘法: x 1 × y 1 x_1×y_1 x1×y1、 x 1 × y 0 x_1 \times y_0 x1×y0、 x 0 × y 1 x_0 \times y_1 x0×y1、 x 0 × y 0 x_0 \times y_0 x0×y0。
3.优化(Karatsuba 分治法)
为进一步优化,可以减少乘法次数。根据 Karatsuba 算法,可以减少到三个乘法:

4.递归求解
每一步乘法都可以递归地按照相同的方式进行分治。最终当子问题的规模足够小(例如规模为 1 时),可以直接通过位运算得到结果。
传统的乘法复杂度是 O ( n 2 ) O(n^2) O(n2),因为有n位,每两位都要相乘。如果不经过第三步的优化,分治后的复杂度还是 O ( n 2 ) O(n^2) O(n2)。

通过分治法,特别是 Karatsuba 算法,可以将两个二进制整数的乘法复杂度从 O ( n 2 ) O(n^2) O(n2)降到 O ( n 1.585 ) O(n^{1.585}) O(n1.585)。这通过将大整数分割为较小的子整数,并递归计算它们的乘积,同时减少直接乘法运算次数来实现。
2.7 Matrix Multiplication 矩阵乘法
矩阵乘法的分治法通过将大矩阵分解为小矩阵递归计算,从而减少复杂度。

- Divide:将两个 n × n n \times n n×n的矩阵A和B分成 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n的子矩阵:
A = [ A 11 A 12 A 21 A 22 ] , B = [ B 11 B 12 B 21 B 22 ] A = \begin{bmatrix} A_{11} & A_{12} \\ A_{21} & A_{22} \end{bmatrix}, \quad B = \begin{bmatrix} B_{11} & B_{12} \\ B_{21} & B_{22} \end{bmatrix} A=[A11A21A12A22],B=[B11B21B12B22]
其中: A 11 , A 12 , A 21 , A 22 A_{11}, A_{12}, A_{21}, A_{22} A11,A12,A21,A22是矩阵A 的 4 个子矩阵,大小均为 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n; B 11 , B 12 , B 21 , B 22 B_{11}, B_{12}, B_{21}, B_{22} B11,B12,B21,B22是矩阵B的 4 个子矩阵。
- Conque递归计算 8 个子矩阵的乘积:
C 11 = A 11 B 11 + A 12 B 21 , C 12 = A 11 B 12 + A 12 B 22 C 21 = A 21 B 11 + A 22 B 21 , C 22 = A 21 B 12 + A 22 B 22 C_{11} = A_{11}B_{11} + A_{12}B_{21}, \quad C_{12} = A_{11}B_{12} + A_{12}B_{22}\quad C_{21} = A_{21}B_{11} + A_{22}B_{21}, \quad C_{22} = A_{21}B_{12} + A_{22}B_{22} C11=A11B11+A12B21,C12=A11B12+A12B22C21=A21B11+A22B21,C22=A21B12+A22B22
通过递归方式计算这些 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n的矩阵乘法。
- Combine将上述结果组合成最终矩阵 C:
C = [ C 11 C 12 C 21 C 22 ] C = \begin{bmatrix} C_{11} & C_{12} \\ C_{21} & C_{22} \end{bmatrix} C=[C11C21C12C22]
这里需要进行 4 次矩阵加法。

现在来分析一下时间复杂度,分解时间复杂度为O(1),只需将矩阵分块。求解需要递归计算 8 次 n 2 × n 2 \frac{n}{2} \times \frac{n}{2} 2n×2n的矩阵乘法,每次乘法复杂度为 T ( n 2 ) T\left(\frac{n}{2}\right) T(2n)。合并需要 O ( n 2 ) O(n^2) O(n2)的时间进行矩阵加法。递归关系为: T ( n ) = 8 T ( n 2 ) + O ( n 2 ) T(n) = 8T\left(\frac{n}{2}\right) + O(n^2) T(n)=8T(2n)+O(n2)。根据主定理,解得: T ( n ) = O ( n 3 ) T(n) = O(n^3) T(n)=O(n3)
改进方向:Strassen 算法通过减少递归中矩阵乘法的次数(从 8 次减少到 7 次),将时间复杂度降低到 O ( n log 2 7 ) ≈ O ( n 2.81 ) O(n^{\log_2 7}) \approx O(n^{2.81}) O(nlog27)≈O(n2.81)。

Strassen算法引入了 7个中间变量(通过标量乘法计算),然后通过这些中间变量,计算最终矩阵 C的元素。通过递归,将矩阵分成更小的子矩阵,使用Strassen方法计算。由于只需 7次乘法而非传统的8次,时间复杂度递归关系变为: T ( n ) = 7 T ( n 2 ) + O ( n 2 ) T(n) = 7T\left(\frac{n}{2}\right) + O(n^2) T(n)=7T(2n)+O(n2)。算法伪代码如下:

结束语
感谢阅读吾之文章,今已至此次旅程之终站 🛬。
吾望斯文献能供尔以宝贵之信息与知识也 🎉。
学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。
然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。
相关文章:
算法分析与设计之分治算法
文章目录 前言一、分治算法divide and conquer1.1 分治定义1.2 分治法的复杂性分析:递归方程1.2.1 主定理1.2.2 递归树法1.2.3 迭代法 二、典型例题2.1 Mergesort2.2 Counting Inversions2.3 棋盘覆盖2.4 最大和数组2.5 Closest Pair of Points2.6 Karatsuba算法&am…...
AI大模型学习笔记|多目标算法梳理、举例
多目标算法学习内容推荐: 1.通俗易懂讲算法-多目标优化-NSGA-II(附代码讲解)_哔哩哔哩_bilibili 2.多目标优化 (python pyomo pareto 最优)_哔哩哔哩_bilibili 学习笔记: 通过网盘分享的文件:多目标算法学习笔记 链接: https://pan.baidu.com…...
【竞技宝】LOL:JDG官宣yagao离队
北京时间2024年12月13日,在英雄联盟S14全球总决赛结束之后,各大赛区都已经进入了休赛期,目前休赛期也快进入尾声,LPL大部分队伍都开始陆续官宣转会期的动向,其中JDG就在近期正式官宣中单选手yagao离队,而后者大概率将直接选择退役。 近日,JDG战队在官方微博上连续发布阵容变动消…...
iOS runtime总结数据结构,消息传递、转发和应用场景
runtime篇 首先看一下runtiem底层的数据结构 首先从objc_class这么一个结构体(数据结构)开始,objc_class继承于objc_object。 objc_object当中有一个成员变量叫isa_t,那么这个isa_t指针就指向一个objc_class类型的类对象ÿ…...
An error happened while trying to locate the file on the Hub and we cannot f
An error happened while trying to locate the file on the Hub and we cannot find the requested files in the local cache. Please check your connection and try again or make sure your Internet connection is on. 关于上述comfy ui使用control net预处理器的报错问…...
力扣刷题TOP101: 32.BM39 序列化二叉树
目录: 目的 思路 复杂度 记忆秘诀 python代码 目的: 请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。 思路…...
modern-screenshot: 一个比html2canvas 性能更好的网页截屏工具
在低代码平台等设计工具中,生成缩略图是非常基础的功能,最开始我们使用的是 html2canvas 工具,但是带来的问题是这个工具非常吃性能,生成缩略图时间动辄6s以上,后来发现 modern-screenshot 这个工具性能会好一些&#…...
使用 GD32F470ZGT6,手写 I2C 的实现
我的代码:https://gitee.com/a1422749310/gd32_-official_-code I2C 具体代码位置:https://gitee.com/a1422749310/gd32_-official_-code/blob/master/Hardware/i2c/i2c.c 黑马 - I2C原理 官方 - IIC 协议介绍 个人学习过程中的理解,有错误&…...
力扣 53. 最大子数组和 (动态规划)
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连续部分。 示例 1: 输入:nums [-2,1,-3,4,-1,2,1,-5,4] 输出:…...
【牛客小白月赛107 题解】
比赛链接 A. Cidoai的吃饭 题目大意 给定一个正整数 n n n,再给定三个正整数 a , b , c a, \ b, \ c a, b, c。初始时 a n s 0 ans 0 ans0。现在开始循环,每次循环按照从上到下的顺序选择第一条符合的执行(即执行完就再从 1. 1. 1. …...
Web day11 SpringBoot原理
目录 1.配置优先级: 2.Bean的管理: bean的作用域: 第三方bean: 方案一: 方案二: SpringBoot原理: 扫描第三方包: 方案1:ComponentScan 组件扫描 方案2࿱…...
JAVA实战:借助阿里云实现短信发送功能
亲爱的小伙伴们😘,在求知的漫漫旅途中,若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界,亦或是读研论文的撰写攻略有所探寻🧐,那不妨给我一个小小的关注吧🥰。我会精心筹备,在…...
【C语言】库函数常见的陷阱与缺陷(3):内存分配函数
目录 一、malloc 函数 1.1. 功能与常见用法 1.2. 陷阱与缺陷 1.3. 安全使用建议 1.4. 安全替代和代码示例 二、calloc 函数 2.1. 功能与常见用法 2.2. 陷阱与缺陷 2.3. 安全使用建议 2.4. 安全替代和代码示例 三、realloc 函数 3.1. 功能与常见用法 3.2. 陷阱与缺…...
探索 Python 编程调试案例:程序平均值的修复过程
💝💝💝Python 作为一门广泛应用的编程语言,其编程过程并非总是一帆风顺。即使是经验丰富的程序员,也会在代码中遇到各种错误。而调试Python代码过程,也是学习中不可避免的步骤。 今天来看一个简单的案例。…...
奇奇怪怪的错误-Tag和space不兼容
报错信息如下: TabError: inconsistent use of tabs and spaces in indentation make: *** [Makefile:24: train] Error 1不能按Tab,要老老实实按space 不过可以在编辑器里面改,把它们调整成一致的;...
linux网络编程 | c | epoll实现IO多路转接服务器
epoll实现IO多路转接服务器 可通过以下视频学习 06-opell函数实现的多路IO转接_哔哩哔哩_bilibili 通过响应式–多路IO转接实现 文章目录 epoll实现IO多路转接服务器1.思路&功能核心思路 2.代码实现multi_epoll_sever.c运行图 1.思路&功能 **功能:**客…...
16-2.Java 反射之 setAccessible 方法详解(Java 访问检查、反射的访问检查、setAccessible())
一、Java 访问检查 1、基本介绍 访问检查就是查看成员使用是否符合访问权限(public、protected、default、private) 2、演示 Person.java package com.my.entity;public class People {private int privateVar 1;int defaultVar 2;protected int …...
颜色的基本处理
数码相机能够获取彩色图像,但相机的色彩处理是一个非常复杂的过程,是非常重要的。 此过程生产制造商在细节方面都是不公布的,但是基本的概念是相同的。当相机捕捉一个真实场景时,是怎么还原成人眼所看到的图像呢? 1.R…...
提升音频转录准确性:VAD技术的应用与挑战
引言 在音频转录技术飞速发展的今天,我们面临着一个普遍问题:在嘈杂环境中,转录系统常常将非人声误识别为人声,导致转录结果出现错误。例如,在whisper模式下,系统可能会错误地转录出“谢谢大家”。本文将探…...
3D一览通在线协同设计,助力汽车钣金件设计与制造数字化升级
汽车行业已迎来智能化的汹涌浪潮,在此背景下,零部件制造商唯有积极应对,以智能制造为核心驱动力,方能跟上行业发展步调,在激烈的市场竞争中抢占先机。作为整车制造不可或缺的核心组件之一,汽车钣金件亦需紧…...
C 进阶 — 字符函数和字符串函数 ( 二 )
C 进阶 — 字符函数和字符串函数 ( 二 ) 书接上回 C 进阶 — 字符函数和字符串函数 ( 一 ) 1.9 strtok 参考资料 strtok 函数用法详解 char * strtok ( char * str, const char * sep );strtok 是 [C 标准库](https://so.csdn.net/so/search?qC 标准库&spm1001.2101.3…...
【Qualcomm】IPQ5018查看连接终端RSSI、SNR、NF方法
IPQ5018 简介 IPQ5018 是高通(Qualcomm)公司推出的一款面向网络设备的系统级芯片(SoC)。它通常用于路由器、接入点和其他网络设备中,提供高性能的无线网络连接。以下是关于 IPQ5018 的一些关键特性和功能: 关键特性 高性能处理器 IPQ5018 集成了多核 CPU,通常是 ARM …...
Windows下编译安装FreeCAD 1.0.0
本文记录在Windows下编译安装FreeCAD 1.0.0的流程。 零、环境 操作系统Windows 11VS Code1.92.1Git2.34.1Visual StudioVisual Studio Community 2022CMake3.22.1 一、编译安装 1.1 安装依赖 从FreeCAD GitHub下载LibPack-1.0.0-v3.0.0-Release.7z,并解压。 1.2…...
Hadoop其一,介绍本地模式,伪分布模式和全分布搭建
目录 一、Hadoop介绍 二、HDFS的本地模式 三、伪分布模式 四、Hdfs中的shell命令 五、全分布搭建 六、使用Java代码操作HDFS 1、环境准备 2、单元测试(Junit)编辑 一、Hadoop介绍 Hadoop 分为三部分 : Common、HDFS 、Yarn、MapRe…...
蓝桥杯刷题——day3
蓝桥杯刷题——day3 题目一题干题目解析代码 题目二题干题目解析代码 题目一 题干 每张票据有唯一的 ID 号,全年所有票据的 ID 号是连续的,但 ID 的开始数码是随机选定的。因为工作人员疏忽,在录入 ID 号的时候发生了一处错误,造…...
【Leecode】Leecode刷题之路第79天之单词搜索
题目出处 79-单词搜索-题目出处 题目描述 个人解法 思路: todo代码示例:(Java) todo复杂度分析 todo官方解法 79-单词搜索-官方解法 方法1:回溯 思路: 代码示例:(Javaÿ…...
海康威视监控web实时预览解决方案
海康威视摄像头都试rtsp流,web页面无法加载播放,所以就得转换成web页面可以播放的hls、rtmp等数据流来播放。 一:萤石云 使用萤石云平台,把rtsp转化成ezopen协议,然后使用组件UIKit 最佳实践 萤石开放平台API文档 …...
virtualbox 搭建ubuntu
环境:VirtualBox-6.1.32 1、下载安装virtualbox 略 2、新建ubuntu 3、配置ubuntu 选择虚拟盘 4、安装ubuntu 5、安装ssh sudo apt install openssh-server openssh-client 查看ip 6、安装samba sudo apt install samba 查看ssh启动状态 sudo systemctl stat…...
RK3588开发笔记-Buildroot编译Qt5WebEngine-5.15.10
目录 前言 一、Qt5WebEngine简介 二、Qt5WebEngine编译 总结 前言 Rockchip RK3588是一款强大的多核处理器,广泛应用于边缘计算、人工智能、嵌入式系统等领域。为了在RK3588上运行自定义的Linux系统,并使用Qt5WebEngine进行Web内容渲染,Buildroot是一个非常合适的工具。本…...
FedAdam算法:供给方信用,数据质量;更新一致性
FedAdam算法:供给方信用,数据质量;更新一致性 FedAdam算法概述 FedAdam是一种联邦学习(Federated Learning)算法。联邦学习是一种机器学习技术,它允许在多个设备或数据中心(称为客户端)上训练模型,而无需将数据集中到一个中央服务器,从而保护数据隐私。FedAdam主要用于…...
webrtc音频模块(三) windows Core Audio API及声音的播放
在前面介绍了ADM(Audio Device Module),它用于抽象音频设备管理和音频数据采集/播放接口。windows的实现是AudioDeviceWinowCode,它封装了Core Audio APIs实现了对音频设备的操作。 Core Audio APIs windows提供了多种音频操作API,比如最常…...
使用ERA5数据绘制风向玫瑰图的简易流程
使用ERA5数据绘制风向玫瑰图的简易流程 今天需要做一个2017年-2023年的平均风向的统计,做一个风向玫瑰图,想到的还是高分辨率的ERA5land的数据(0.1分辨率,逐小时分辨率,1950年至今)。 风向,我分为了16个&…...
深度优先遍历(DFS)
深度优先遍历(DFS) 1. 计算布尔二叉树的值2. 求根节点到叶节点数字之和3.二叉树剪枝4.验证二叉搜索树5. 二叉搜索树中第 K 小的元素6. 二叉树的所有路径 深度优先遍历(DFS,全称为Depth First Traversal),是…...
国科大网络协议安全期末
完整资料仓库地址:https://gitee.com/etsuyou/UCAS-Network-Protocol-Security 部分题目: 六 论述题10*220 试讨论IPv6解决了IPv4的哪些“痛点”,以及IPv6存在的安全问题试比较IPsec与SSL的安全性 五 简答题5*315 简述MAC欺骗和ARP欺骗的…...
开源密码管理器 Bitwarden 一站式管理所有密码以及 2FA
本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 随着注册的平台越来越多,管理密码的难度也越来越高了。要是把密码都设置成一样的,担心哪天某个平台泄露被一锅端,而每个平台单独一个密码又不太好记,这时候就…...
Python爬虫之Selenium的应用
【1】Selenium基础介绍 1.什么是selenium? (1)Selenium是一个用于Web应用程序测试的工具。 (2)Selenium 测试直接运行在浏览器中,就像真正的用户在操作一样。 (3)支持通过各种driv…...
华为无线AC、AP模式与上线解析(Huawei Wireless AC, AP Mode and Online Analysis)
华为无线AC、AP模式与上线解析 为了实现fit 瘦AP的集中式管理,我们需要统一把局域网内的所有AP上线到AC,由AC做集中式管理部署。这里我们需要理解CAPWAP协议,该协议分为两种报文:1、管理报文 2、数据报文。管理报文实际在抓包过程…...
k8s中用filebeat文件如何收集不同service的日志
以下是一个详细的从在 Kubernetes 集群中部署 Filebeat,到实现按web-oper、web-api微服务分离日志并存储到不同索引的完整方案: 理解需求:按服务分离日志索引 在 Kubernetes 集群中,有web-oper和web-api两种微服务,希…...
linux常用命令(cd、ls)
命令cd cd 是 Linux 系统中用于改变当前工作目录的命令。它是 "change directory" 的缩写。以下是关于 cd 命令的详细解释和使用方法: 基本用法 cd [目录路径]:将当前工作目录切换到指定的目录路径。 常用选项与示例 1、切换到指定目录 …...
Java实现一个带头节点的单链表
什么是单链表? 单链表是一种基础的数据结构,其中每个节点都包含两部分: 数据域:存储节点数据。指针域:存储指向下一个节点的引用。 为什么使用头节点? 头节点的存在简化了操作逻辑: 统一操作…...
代码随想录-算法训练营-番外(图论01:图论理论基础,所有可到达的路径)
day01 图论part01 今日任务:图论理论基础/所有可到达的路径 代码随想录图论视频部分还没更新 https://programmercarl.com/kamacoder/图论理论基础.html#图的基本概念 day01 所有可达路径 邻接矩阵 import java.util.Scanner;import java.util.List;import java.util.ArrayL…...
js:我要在template中v-for循环遍历这个centrerTopdata,我希望自循环前面三个就可以了怎么写
问: 我按在要在template中v-for循环遍历这个centrerTopdata,我希望自循环前面三个就可以了怎么写? 回答: 问: <div v-for"(item, index) in centrerTopdata.slice(0, 3)" :key"index"> d…...
软考高级架构 - 10.5 软件架构演化评估方法
10.4 软件架构演化原则总结 本节提出了18条架构演化的核心原则,并为每条原则设计了简单而有效的度量方法,用于从系统整体层面提供实用信息,帮助评估和指导架构演化。 演化成本控制:成本小于重新开发成本,经济高效。进…...
40 list类 模拟实现
目录 一、list类简介 (一)概念 (二)list与string和vector的区别 二、list类使用 (一)构造函数 (二)迭代器 (三)list capacity (四&#x…...
【原生js案例】如何实现一个穿透字体颜色的导航
普通的导航大家都会做,像这种穿透字体的导航应该很少见吧。高亮不是通过单独设置一个active类来设置字体高亮颜色,鼠标滑过导航项,字体可以部分是黑色,不分是白色,这种效果的实现 感兴趣的可以关注下我的系列课程【we…...
(RHCE)工程师学习考证
如果你像我一样,非科班出身且对 IT 行业知识储备几乎为零,却立志考取 RHCE 红帽工程师证书,那么以下这份学习教程或许能助你一臂之力。 首先,要对 RHCE 有个基本的认识。RHCE 是红帽企业级 Linux 认证,它侧重于实际操作…...
Nuxt3 axios封装 使用axios接口请求
一、先安装axios npm install add axios 封装请求request.ts文件 import axios from axios import { ElMessage, Message } from "element-plus" import {getToken} from ./token.js const service axios.create({baseURL:/api,//本地使用 }) service.interceptor…...
东方通TongWeb替换Tomcat的踩坑记录
一、背景 由于信创需要,原来项目的用到的一些中间件、软件都要逐步替换为国产品牌,决定先从web容器入手,将Tomcat替换掉。在网上搜了一些资料,结合项目当前情况,考虑在金蝶AAS和东方通TongWeb里面选择,后又…...
引用类型集合的深拷贝,无需手动写循环:Apache Commons Lang (SerializationUtils)
在java中,我们如果想要对引用类型的集合进行深拷贝。有一种方式,就是调用SerializationUtils Apache Commons Lang (SerializationUtils) Apache Commons Lang 提供了 SerializationUtils 类,可以利用 Java 的序列化机制来进行集合及其元素…...
高阶函数:JavaScript 编程中的魔法棒
在JavaScript的世界里,高阶函数是一种强大的工具,它允许我们将函数作为参数传递或将函数作为返回值。这种特性使得JavaScript代码更加灵活和强大。本文将深入探讨高阶函数的定义、用法以及在实际项目中的最佳实践,帮助大家更好地理解和应用这…...