misc
拿了字节offer好开心. 10号考算法, 复习加水一篇博客. 希望不要被八强啊;
注: 以下代码都是直接再typora上敲的, 不保证其正确性
引言
什么是算法及其特征
算法是通过一个有限的指令序列集合对特定的问题进行求解的一种计算执行描述。
(1)输入:一个算法具有零个或多个取自指定集合的 输入值;
(2)输出:对算法的每一次输入,算法具有一个或多个与输入值相联系的输出值
(3)确定性:算法的每一个指令步骤都是明确的;
(4)有限性:对算法的每一次输入,算法都必须在有 限步骤(即有限时间)内结束;
(5)正确性:对每一次输入,算法应产生出正确的输 出值;
(6)通用性:算法的执行过程可应用于所有同类求解 问题,而不是仅适用于特殊的输入。
问题实例和问题规模
问题实例是计算问题的解决方案所需的全部输入
问题规模是算法的输入实例大小。
算法初步
插入排序算法
loop invariant: 当前指针左边数组已排序
1 | void insertionSort(vector<int> &nums) { |
分析: 整体复杂度显然为一个三角形, 面积为O(n^2), 对于基本有序的输入友好(O(n), 因为j指针动不了);
最坏O(n^2)、最好O(n^2)和平均O(n)情形复杂性
归并排序算法
可以用来计算逆序对;
归并的基本思想是分治, 而分治思想主要分为三个部分:
- divide - 归并中的取中间下标
- conquer- 递归调用
- combine -两个有序子数组的merge
同样使用分治思想的还有快速排序(combine过程无需做), 平面上找最近点对.
1 | void merge(vector<int> &nums, int lo, int mid, int hi) { |
时间复杂度为O(nlogn), 空间复杂度为: 递归栈logn, 辅助数组总长度On, 故为O(n)
常见排序算法复杂度:
堆排序
堆的逻辑结构是完全二叉树, 实际存储结构是数组
对于一个结点i, 其父亲为i/2, 左孩子为2 * i + 1, 右孩子为2 * i + 2;
堆主要核心的代码就是swim 和 sink, 具体就不写了.
中位数和顺序统计
最大最小值
单独求一个无序数组的最值, 比较次数显然为n - 1, 代码如下:
1 | int maxVal(vector<int> &nums) { |
同时求最大最小值:
- 分别求: 则比较次数为 n - 1 + n - 2 = 2n - 3;
- 同时求显然可以减少比较次数, 循环执行了 n / 2次, 每次循环内3次比较, 所以只比较了3n / 2次;
1 | vector<int> minAndMax(vector<int> &nums) { |
topk问题: 期望时间为线性的选择
选出无序序列中第k大 或者 前k大个数字的时候 一定 一定使用这种方法(能放入内存的情况下)
1 | int topk(vector<int> &nums, int lo, int hi, int k) { |
时间复杂度O(n)
topk问题: 最坏时间为线性的选择
这算法有点难, 一般面试也不会用上, 随便看看吧
红黑树
红黑树是一种放宽了限制的avl树(通过引入红节点), 所以查询性能比avl树弱, 但是插入删除旋转次数较少, 它有如下五个基本性质:
- 每个结点红或者黑
- 根为黑
- nil结点为黑
- 若节点为红, 则两个孩子必为黑(即没有两个红节点直接连接)
- 每个结点到所有后代叶子的路径有同样多的黑结点(黑高相等)
一棵n个内点的红黑树的高度至多 是2log(n+1)。
插入算法的时间、至多使用2次旋转
删除算法的时间、至多使用3次旋转
红黑树左旋
红黑树插入
step 1:将z节点按BST树规则插入红黑树中, z是叶子节点;
step 2:将z涂红;
step 3:调整使其满足红黑树的性质;
OS树
os树就是红黑树维护了size属性;
OS-select(x, i)
: 选择以x为根的子树中, 查找第i个最小元素
1 | TreeNode *OSSelect(TreeNode *x, int i) { |
OSRank(TreeNode *x)
: 在以x为根的自述中求x的秩:
不相交集合的数据结构
主要是并查集, 主要用来计算连通分量个数, 计算朋友圈…
1 | class UF { |
图论
BFS和DFS算法
- 白色、灰色和黑色结点概念和作用
- 所有节点一开始被初始化为白色, 颜色属性保存再结点color域中
- 所有与黑色结点邻接的结点都已经被发现
- 灰色结点是代表的是已访问和未访问两个集合的边界
1 | enum {white = 0, gray = 1, black = 2}; |
- 计算过程及其时间复杂度