目录
一,题目类型
常见的题目分为几大类:
1,算法
分治、搜索、贪心、DP
2,数据结构
链表、二叉树、并查集、树状数组、线段树
3,数学
数论、几何、图论
4,模拟
二,时间复杂度
时间复杂度第四章
三,模板积累
四,STL
五,压行坑之类型坑
我在力扣OJ 1314. 矩阵区域和 里面,我写了一个小函数:
int f(vector<vector<int>> &mat, int i, int j)
{
if (i < 0 || j < 0) return 0;
if (i >= mat.size()) i = mat.size() - 1;
if (j >= mat[0].size()) j = mat[0].size() - 1;
return mat[i][j];
}
这是一个已经AC的代码里面的一个函数,然而,故事并没有结束。
为了压缩行,我改成了:
int f(vector<vector<int>> &mat, int i, int j)
{
return (i < 0 || j < 0) ? 0 : mat[min(i,mat.size() - 1)][min(j,mat[0].size() - 1)];
}
结果编译错误,为什么呢?
因为size函数返回的是unsigned long
我改成:
int f(vector<vector<int>> &mat, unsigned int i,unsigned int j)
{
return (i < 0 || j < 0) ? 0 : mat[min(i,mat.size() - 1)][min(j,mat[0].size() - 1)];
}
还是编译错误,再改成
int f(vector<vector<int>> &mat, unsigned long i,unsigned long j)
{
return (i < 0 || j < 0) ? 0 : mat[min(i,mat.size() - 1)][min(j,mat[0].size() - 1)];
}
结果编译成功但是居然解答错误。
仔细一看,这个代码就是一坨垃圾,i<0根本就是不可能的。
如果非要压缩行的话,只能做类型转换。
六,算法形态
1,生命周期
从生命周期的角度,我们对常见算法的经典形态做一个梳理。
生命周期一般包括初始化、更新、查询三个过程。
(1)分治、贪心支持单次查询
(2)搜索、动态规划支持多次查询
(3)树状数组支持多次单点更新、多次区间查询。
(4)线段树支持多次单点更新、多次区间查询,也支持多次区间更新、多次区间查询。
(5)很多树、优先队列、哈希表都支持多次更新、多次查询。
2,持久化
如果有大量查询,为了使得单次查询变得高效,我们需要对算法做持久化。
按照查询模式分为两种,离线查询是一次把大量查询请求全部输入,一次性输出所有查询结果,而在线查询是需要提供单次查询接口,这个接口会被调用很多次。
(1)硬编码
如果单次查询时间长,但是总查询情况数比较少,可以用硬编码。
在我的博文中搜索“硬编码”、“二次编程”、“二次编码”即可。
第一类,对第一次编码输出的数组完成硬编码(即二次编码)
第二类,对手动计算出的数组或者数字完成硬编码
UVALive 2701 Find The Multiple
(2)二次编码(二次编程)
大部分二次编码的目的是完成硬编码,也有的是为了逻辑的重新整理。
(3)可持久化并查集
(3)可持久化字典树
(4)可持久化线段树
3,离散化
(1)数据的离散化
(2)解空间的离散化
持久化的代价是空间复杂度太高,为了均衡时间和空间,可以对持久空间做离散化。