主定理解析
结论
分析
递归公式通用形式
\(T(n) = aT(\frac{n}{b}) + O(n^d)\)
最后的时间复杂度和这a, b ,d几个参数有关。
把递归问题每一层的问题数目,计算量都一一列出,可以得到如下公式
最终的总规模是等比数列,因为初始问题规模n是固定的,d是规定的,将\(O(n^d)\)看作常数,公比是\(\frac{a}{b^d}\)
情况一:公比小于1
时间复杂度由第一项确定
情况二:公比大于1
时间复杂度由最后一项确定
化简过程:
公比等于1
二分搜索
大整数乘法
Strassen矩阵乘法
棋盘覆盖
归并排序
思想
- “分”:分割成小序列,一直分割直到只有1个元素,然后回退
- 治:小序列两个两个合并成大序列
- 开一个辅助数组B
- 这时待排数组为空,利用两个指针p和q分别指向待归并的两个数组的起点,第三个指针r指向辅助数组,将p和q中较大的放入辅助数组B,比如如果是p,然后移动p指针,另一个指针q保持不变
- 如果其中一个指针移动的位置超过了原来对应待归并的数组的范围,将剩下一个数组里元素全部拷贝回待排数组
代码
1 | public static void mergeSort(int[] data, int start, int end) { |
快速排序
线性时间选择
问题描述
给定线性序集中n个元素和一个正数k,1 ≤ k ≤ n,要求找出这n个元素中第k小的元素
RandomizedSelect算法
算法思路
初始化:给定数组
a
,范围从p
到r
,以及要查找的第k
小的元素。基线条件:如果数组的长度小于等于
k
,则说明第k
小的元素一定在这个数组中。随机选择枢纽:在数组
a[p:r]
中随机选择一个元素i
。划分数组:根据枢纽
i
的值,将数组划分为两个子数组:a[p:j]
:包含小于i
的元素。a[j+1:r]
:包含大于或等于i
的元素。
递归查找:
- 如果左侧小于等于枢纽的子数组的长度小于k,则在右侧大于枢纽的子数组
a[j+1:r]
中递归查找第k
小的元素。 - 如果左侧小于等于枢纽的子数组的长度大于等于k,则在左侧小于等于枢纽的子数组
a[p:j]
中递归查找第k - (j - start + 1)
小的元素。
- 如果左侧小于等于枢纽的子数组的长度小于k,则在右侧大于枢纽的子数组
结束条件:当子数组的长度减少到1时,即
p = r
,此时的元素就是第k
小的元素。
举例:
代码
1 | public static int partion(int[] data, int k, int start, int end) { |
时间复杂度分析
Select算法
算法思路
分组:将原数组分成 \(\lceil \frac{n}{5} \rceil\) 组,每组包含5个元素。如果数组的总元素数不能被5整除,最后一个组的元素数将小于5。
每组排序:对每组的5个元素进行排序。
找每组中位数:在每组排序后的元素中,找到每组的中位数。如果组内的元素个数是奇数,则中位数是中间的元素;如果是偶数,则中位数是中间两个元素中较大的那个。
递归找到基准:将所有组的中位数组合起来,形成一个新数组。如果这个新数组的元素个数是奇数,直接取中间的元素作为基准 \(x\);如果是偶数,则取中间两个元素中较大的那个作为基准 \(x\)。这个过程可以通过递归调用快速选择算法来实现。
划分数组:根据基准 \(x\) 的值,将原数组划分为两部分:
- 小于 \(x\) 的元素。
- 大于或等于 \(x\) 的元素。
递归处理:
- 如果要查找的第 \(k\) 小的元素在基准 \(x\) 的左侧,则在左侧的子数组中递归查找第 \(k\) 小的元素。
- 如果 \(k\) 大于基准 \(x\) 在数组中的位置(即 \(k > \lceil \frac{n}{2} \rceil\)),则在右侧的子数组中递归查找第 \(k - \lceil \frac{n}{2} \rceil\) 小的元素。
结束条件:当子数组的长度减少到1时,即找到了第 \(k\) 小的元素。
这种“中位数的中位数”算法可以减少算法的最坏情况时间复杂度,使其在最坏情况下也能保持较好的性能。
举例: