6)哈希表
7)树
第二章
树
(2-1)画解树
更多内容请收看:画解树。
8)二叉树
(2-2)画解二叉树
为了增加阅读体验,更多内容请收看:画解二叉树。
9)二叉搜索树
10)堆
(2-4)画解堆
为了增加阅读体验,更多内容请收看:画解堆。
11)AVL树
(2-5)画解AVL树
本文已超五万字,为了增加阅读体验,更多内容请收看:画解二叉平衡树。
12)线段树
(2-6)画解线段树
为了增加阅读体验,更多内容请收看:画解线段树。
13)字典树
(2-7)画解字典树
为了增加阅读体验,更多内容请收看:画解字典树。
14)霍夫曼树
(2-8)画解霍夫曼树
为了增加阅读体验,更多内容请收看:画解霍夫曼树。
15)并查集
(2-9)画解并查集
为了增加阅读体验,更多内容请收看:画解并查集。
16)图
第三章
图
(3-1)画解图
本文已超五万字,为了增加阅读体验,更多内容请收看:画解图。
17)二分匹配
(3-2)画解二分匹配
为了增加阅读体验,更多内容请收看:画解二分匹配。
18)最短路
(3-3)画解最短路
为了增加阅读体验,更多内容请收看:画解最短路。
19)最小生成树
(3-4)画解最小生成树
为了增加阅读体验,更多内容请收看:画解最小生成树。
20)强连通
(3-5)画解强连通
为了增加阅读体验,更多内容请收看:画解强连通。
4、算法入门
- 算法入门,其实就是要开始我们的刷题之旅了。先给出思维导图,然后一一介绍入门十大算法。
5、算法进阶
- 算法进阶这块是我打算规划自己未来十年去完成的一个项目,囊括了 大学生ACM程序设计竞赛、高中生的OI竞赛、LeetCode 职场面试算法 的算法全集,也就是之前网络上比较有名的 《夜深人静写算法》 系列,这可以说是我自己对自己的一个要求和目标吧。
- 如果只是想进大厂,那么 算法入门 已经足够了,不需要再来看算法进阶了,当然如果对算法有浓厚兴趣,也欢迎和我一起打卡。由于内容较难,工作也比较忙,所以学的也比较慢,一周基本也只能更新一篇。
这个系列主要分为以下几个大块内容:
1)图论
2)动态规划
3)计算几何
4)数论
5)字符串匹配
6)高级数据结构(课本上学不到的)
7)杂项算法
- 先来看下思维导图,然后我大致讲一下每一类算法各自的特点,以及学习方式:
1)图论
1、搜索概览
- 图论主要围绕搜索算法进行展开。搜索算法的原理就是枚举。利用计算机的高性能,给出人类制定好的规则,枚举出所有可行的情况,找到可行解或者最优解。
- 比较常见的搜索算法是 深度优先搜索(又叫深度优先遍历) 和 广度优先搜索(又叫广度优先遍历 或者 宽度优先遍历)。各种图论的算法基本都是依靠这两者进行展开的。
2、深度优先搜索
- 深度优先搜索一般用来求可行解,利用剪枝进行优化,在树形结构的图上用处较多;而广度优先搜索一般用来求最优解,配合哈希表进行状态空间的标记,从而避免重复状态的计算;
- 原则上,天下万物皆可搜,只是时间已惘然。搜索会有大量的重复状态出现,这里的状态和动态规划的状态是同一个概念,所以有时候很难分清到底是用搜索还是动态规划。
- 但是,大体上还是有迹可循的,如果这个状态不能映射到数组被缓存下来,那么大概率就是需要用搜索来求解的。
- 如图所示,代表的是一个深度优先搜索的例子,红色实箭头表示搜索路径,蓝色虚箭头表示回溯路径。
- 红色块表示往下搜索,蓝色块表示往上回溯,遍历序列为:
0 -> 1 -> 3 -> 4 -> 5 -> 2 -> 6
- 同样,搜索的例子还有:
- 计算的是利用递归实现的
n
n
n 的阶乘。
3、记忆化搜索
- 对于斐波那契函数的求解,如下所示:
- f
(
n
)
=
{
1
(
n
=
0
)
1
(
n
=
1
)
f
(
n
−
1
)
f
(
n
−
2
)
(
n
2
)
f(n) = \begin{cases}1 & (n = 0) \1 & (n = 1) \f(n-1) + f(n-2) & (n > 2) \end{cases}
f(n)=⎩⎪⎨⎪⎧11f(n−1)+f(n−2)(n=0)(n=1)(n>2)
- 对于
f
(
5
)
f(5)
f(5) 的求解,程序调用如下:
- 这个过程用到了很多重复状态的搜索,我们需要将它优化,一般将一些状态缓存起来。
- 我们通过一个动图来感受一下:
- 当第二次需要计算
f
(
2
)
f(2)
f(2) 和
f
(
3
)
f(3)
f(3) 时,由于结果已经计算出来并且存储在
h
[
2
]
h[2]
h[2] 和
h
[
3
]
h[3]
h[3] 中,所以上面这段代码的fib != inf
表达式为真,直接返回,不再需要往下递归计算,这样就把原本的 “递归二叉树” 转换成了 “递归链”, 从而将原本指数级的算法变成了多项式级别。
- 这就是记忆化搜索,像这种把状态缓存起来的方法,就是动态规划的思想了。
4、广度优先搜索
- 单向广搜就是最简化情况下的广度优先搜索(Breadth First Search),以下简称为广搜。游戏开发过程中用到的比较广泛的 A* 寻路,就是广搜的加强版。
- 我们通过一个动图来对广搜有一个初步的印象。
- 从图中可以看出,广搜的本质还是暴力枚举。即对于每个当前位置,枚举四个相邻可以行走的方向进行不断尝试,直到找到目的地。有点像洪水爆发,从一个源头开始逐渐蔓延开来,直到所有可达的区域都被洪水灌溉,所以我们也把这种算法称为 FloodFill。
- 那么,如何把它描述成程序的语言呢?这里需要用到一种数据结构 —— 队列。
- 这时候,算法和数据结构就完美结合了。
2)动态规划
动态规划算法三要素:
①所有不同的子问题组成的表;
②解决问题的依赖关系可以看成是一个图;
③填充子问题的顺序(即对②的图进行拓扑排序,填充的过程称为状态转移);
- 如果子问题的数目为
O
(
n
t
)
O(n^t)
O(nt),每个子问题需要用到
O
(
n
e
)
O(n^e)
O(ne) 个子问题的结果,那么我们称它为 tD/eD 的问题,于是可以总结出四类常用的动态规划方程:(下面会把opt作为取最优值的函数(一般取
m
i
n
min
min 或
m
a
x
max
max ),
w
(
j
,
i
)
w(j, i)
w(j,i)为一个实函数,其它变量都可以在常数时间计算出来)。
1、1D/1D
- d
[
i
]
=
o
p
t
(
d
[
j
]
w
(
j
,
i
)
∣
0
<
=
i
<
j
)
d[i] = opt( d[j] + w(j, i) | 0 <= i < j )
d[i]=opt(d[j]+w(j,i)∣0<=i<j)
- 状态转移如图四所示(黄色块代表
d
[
i
]
d[i]
d[i],绿色块代表
d
[
j
]
d[j]
d[j]):
- 这类状态转移方程一般出现在线性模型中。
2、2D/0D
- d
[
i
]
[
j
]
=
o
p
t
(
d
[
i
−
1
]
[
j
]
x
i
,
d
[
i
]
[
j
−
1
]
y
j
,
d
[
i
−
1
]
[
j
−
1
]
z
i
j
)
d[i][j] = opt( d[i-1][j] + x_i, d[i][j-1] + y_j, d[i-1][j-1] + z_{ij} )
d[i][j]=opt(d[i−1][j]+xi,d[i][j−1]+yj,d[i−1][j−1]+zij)
- 状态转移如图四所示:
- 比较经典的问题是最长公共子序列、最小编辑距离。
- 有关最长公共子序列的问题,可以参考以下文章:夜深人静写算法(二十一)- 最长公共子序列
- 有关最小编辑距离的问题,可以参考以下文章:夜深人静写算法(二十二)- 最小编辑距离
3、2D/1D
- d
[
i
]
[
j
]
=
w
(
i
,
j
)
o
p
t
(
d
[
i
]
[
k
−
1
]
d
[
k
]
[
j
]
)
d[i][j] = w(i, j) + opt( d[i][k-1] + d[k][j] )
d[i][j]=w(i,j)+opt(d[i][k−1]+d[k][j])
- 区间模型常用方程,如图所示:
- 另外一种常用的 2D/1D 的方程为:
- d
[
i
]
[
j
]
=
o
p
t
(
d
[
i
−
1
]
[
k
]
w
(
i
,
j
,
k
)
∣
k
<
j
)
d[i][j] = opt( d[i-1][k] + w(i, j, k) | k < j )
d[i][j]=opt(d[i−1][k]+w(i,j,k)∣k<j)
-
- 区间模型的详细内容可以参考以下这篇文章:夜深人静写算法(二十七)- 区间DP
4、2D/2D
- d
[
i
]
[
j
]
=
o
p
t
(
d
[
i
′
]
[
j
′
]
w
(
i
′
,
j
′
,
i
,
j
)
∣
0
<
=
i
′
<
i
,
0
<
=
j
′
<
j
)
d[i][j] = opt( d[i’][j’] + w(i’, j’, i, j) | 0 <= i’ < i, 0 <= j’ < j)
d[i][j]=opt(d[i′][j′]+w(i′,j′,i,j)∣0<=i′<i,0<=j′<j)
- 如图所示:
- 常见于二维的迷宫问题,由于复杂度比较大,所以一般配合数据结构优化,如线段树、树状数组等。
- 对于一个tD/eD 的动态规划问题,在不经过任何优化的情况下,可以粗略得到一个时间复杂度是
O
(
n
t
e
)
O(n^ {t+e})
O(nt+e),空间复杂度是
O
(
n
t
)
O(n^t)
O(nt) 的算法,大多数情况下空间复杂度是很容易优化的,难点在于时间复杂度,后续章节将详细讲解各种情况下的动态规划优化算法。
3)计算几何
- 计算几何的问题是代码量最大的。它是计算机科学的一个分支,以往的解析几何,是用代数的方法,建立坐标系去解决问题,但是很多时候需要付出一些代价,比如精度误差,而计算几何更多的是从几何角度,用向量的方法来尽量减少精度误差,例如:将除法转化为乘法、避免三角函数等近似运算 等等。
- 如果一个比赛中,有一道计算几何的题,那么至少,它不会是一道水题。
1、double 代替 float
- c++ 中 double 的精度高于 float,对精度要求较高的问题,务必采用 double;
2、浮点数判定
- 由于浮点数(小数)中是有无理数的,即无限不循环小数,也就是小数点后的位数是无限的,在计算机存储的时候不可能全部存下来,一定是近似的存储的,所以浮点数一定是存在精度误差的(实际上,就算是有理数,也是存在误差的,这和计算机存储机制有关,这里不再展开,有兴趣可以参见我博客的文章:C++ 浮点数精度判定);
- 两个浮点数是否相等,可以采用两数相减的绝对值小于某个精度来实现:
const double eps = 1e-8;
bool EQ(double a, double b) {
return fabs(a - b) < eps;
}
- 并且可以用一个三值函数来确定某个数是零、大于零还是小于零:
int threeValue(double d) {
if (fabs(d) < eps)
return 0;
return d > 0 ? 1 : -1;
}
3、负零判定
- 因为精度误差的存在,所以在输出的时候一定要注意,避免输出 -0.00:
double v = -0.0000000001;
printf("%.2lf\n", v);
- 避免方法是先通过三值函数确定实际值是否为0,如果是0,则需要取完绝对值后再输出:
double v = -0.0000000001;
if(threeValue(v) == 0) {
v = fabs(v);
}
printf("%.2lf\n", v);
4、避免三角函数、对数、开方、除法等
- c++ 三角函数运算方法采用的是 CORDIC算法,一种利用迭代的方式进行求解的算法,其中还用到了开方运算,所以实际的算力消耗还是很大的,在实际求解问题的过程中,能够避免不用就尽量不用。
- 除法运算会带来精度误差,所以能够转换成乘法的也尽量转换为乘法运算。
5、系统性的学习
基础知识:点、向量、叉乘、点乘、旋转、线段、线段判交、三角形面积;
进阶知识:多边形面积、凸多边形判定、点在多边形内判定;
相关算法:二维凸包、三维凸包、旋转卡壳、多边形面积交、多边形面积并、多边形面积异或、多边形和圆的面积交、半平面交、最小覆盖圆、最小包围球、模拟退火。
还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!
王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。
对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!
【完整版领取方式在文末!!】
93道网络安全面试题
内容实在太多,不一一截图了
黑客学习资源推荐
最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
1️⃣零基础入门
① 学习路线
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
② 路线对应学习视频
同时每个成长路线对应的板块都有配套的视频提供:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!