基础算法。
复杂度
关于复杂度,在算法竞赛中的重要性不言而喻。在最开始轻视复杂度的重要程度加上懒惰和弱小的比如我也在因为复杂度狂挂的悲惨经历中意识到这个东西的必要,所以在这里总结一下。
时间复杂度
关于一些符号,不常用,参见 OI-wiki。
我们较为常用的为 \(O\) 符号,即函数的渐进上界。
一些简单的复杂度计算:
1. 首先是无脑数循环,略。
2. 关于 dfs 一张 n 个点,m 条边的图,时间复杂度为 \(O(n+m)\) 。
3. 主定理(Master Theorem):\(T(n)=aT(\frac{n}{b})+f(n)\)
主要用于计算递归的复杂度。
证明:将规模为 n 的问题,分解为 a 个规模为 \((\frac{n}{b})\) 的问题,依次合并,每次合并的耗时为 \(f(n)\) 。
4. 均摊复杂度:
成因:算法进行过程中对于内存中数据的更改对后续的影响。
\(O(均摊)=\dfrac{多次操作的总复杂度}{总操作次数}\)
势能分析:求均摊上界。有点玄学,用物理上的势能来类比理解,感性理解一下吧大概。呃呃概括不出来qwq
空间复杂度
int:4 Bytes
long long:8 Bytes
以内存限制为 256 MB 为例,数组最大开到 64e6。
依此类推。
枚举
略。
模拟
略。
递归 & 分治
递归
两个重要特征:结束条件、自我调用。
优点:条理清晰,可读性强。便于理解结构。
缺点:层数过多会栈溢出。(递归的实现依靠堆栈)
注意:似乎在哪看到过递归函数定义的时候用 inline 容易出事(?)
还有就是一句很对的话。
明白一个函数的作用并相信它能完成这个任务,千万不要跳进这个函数里面企图探究更多细节。 否则就会陷入无穷的细节无法自拔,人脑能压几个栈啊。
分治
分而治之。
贪心
通过每一步的当前最优,取得整体的最优解。
证明比较重要,容易假。
常用证明方法通常是反证法和归纳法,不表。
排序解法:需要严谨证明,否则容易寄。
后悔解法( OI-wiki 的奇怪名称,但是似乎也挺贴切?):类似于 b/dfs ,每种都尝试一下然后比较。常于 dp 结合使用。
贪心算法与动态规划的不同:
贪心对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。
排序
分类极多,不表。
一些性质:
稳定性
稳定性即相等的元素经过排序之后相对顺序是否发生了改变。
稳定排序:基数排序、计数排序、插入排序、冒泡排序、归并排序……
非稳定排序:选择排序、堆排序、快速排序……
选择排序/冒泡排序:\(O(n^2)\)
插入排序:\(O(n^2)\)
数列有序时适宜使用插排,复杂度可降到 \(O(n)\)。
计数排序:
- 计算每个数出现了几次;
- 求出每个数出现次数的前缀和;
- 利用出现次数的前缀和,从右至左计算每个数的排名。
\(O(n+w)\) ,其中 \(w\) 表示待排数据的值域大小。
基数排序:
将待排序的元素拆分为 k 个关键字(比较两个元素时,先比较第一关键字,如果相同再比较第二关键字……),然后先对第 k 关键字进行稳定排序,再对第 k-1 关键字进行稳定排序,再对第 k-2 关键字进行稳定排序……最后对第一关键字进行稳定排序,这样就完成了对整个待排序序列的稳定排序。
\(O(kn+\sum\limits_{i=1}^k w_i)\),其中 \(w_i\) 为其中第 \(i\) 关键字的值域大小。
快速排序:
最优时间复杂度/平均时间复杂度:\(O(nlogn)\)
最坏时间复杂度:\(O(n^2)\)
归并排序:
基于分治思想将数组分段排序后合并。是一种高效的、基于比较排序的排序方式。
时间复杂度:\(O(nlogn)\)
堆排序:
首先建立大顶堆,然后将堆顶的元素取出,作为最大值,与数组尾部的元素交换,并维持残余堆的性质;
之后将堆顶的元素取出,作为次大值,与数组倒数第二位元素交换,并维持残余堆的性质;
以此类推,在第 n-1 次操作后,整个数组就完成了排序。
时间复杂度:\(O(nlogn)\)
桶排序:
- 设置一个定量的数组当作空桶;
- 遍历序列,并将元素一个个放到对应的桶中;
- 对每个不是空的桶进行排序;
- 从不是空的桶里把元素再放回原来的序列中。
平均时间复杂度:\(O(n+\frac{ n^2}{k}+k)\)
最坏:\(O(n^2)\)
希尔排序:
- 将待排序序列分为若干子序列(每个子序列的元素在原始数组中间距相同);
- 对这些子序列进行插入排序;
- 减小每个子序列中元素之间的间距,重复上述过程直至间距减少为 1。
希尔排序的平均时间复杂度和最坏时间复杂度与间距序列的选取(就是间距如何减小到 1)有关,比如「间距每次除以 3」的希尔排序的时间复杂度是 \(O(n^{3/2})\)。已知最好的最坏时间复杂度为 \(O(nlog^2n)\)。
锦标赛排序:
锦标赛排序(英文:Tournament sort),又被称为树形选择排序,是选择排序的优化版本,堆排序的一种变体(均采用完全二叉树)。它在选择排序的基础上使用优先队列查找下一个该选择的元素。
该算法的名字来源于单败淘汰制的竞赛形式。在这种赛制中有许多选手参与比赛,他们两两比较,胜者进入下一轮比赛。这种淘汰方式能够决定最好的选手,但是在最后一轮比赛中被淘汰的选手不一定是第二好的——他可能不如先前被淘汰的选手。
时间复杂度:\(O(nlogn)\)
初始化 \(O(n)\),从 \(n\) 个元素中选一个 \(O(logn)\)。