
为了给人工智能的学习打下良好基础,我于今年5月份开始进行程序语言、数据结构和算法的学习。至今为止,熟悉了C/C++的简单用法,常见数据结构(数组、链表、栈、队列、树、图、堆、散列表等)的实现,基本算法(二分查找、滑动窗口、递归、深度/广度优先搜索、贪心、动态规划、最短路)的运用。能够使用C/C++解决简单的现实问题和算法题目,具备了一定的编程能力。
现将过去几个月的学习做梳理和总结,记录学习的经验和有关的思考,发现短板和不足,为接下来的学习指明方向、
在我看来,程序语言和数据结构/算法完全是两种东西,但又彼此紧密相连。程序语言是一种描述性工具,而数据结构/算法是我们要描述的内容。程序语言相当于笔,而数据结构/算法是我们要画的东西。数据结构/算法是内容,而程序语言是工具、载体。
我们用程序语言可以写各种东西,也可以用自然语言、程序框图等去描述算法。但是运用程序语言去实现算法和数据结构,毫无疑问是最为主要的方式。如果不能熟练地掌握程序语言,我们就无法把我们想的内容描述出来,也就无法实现数据结构和算法。如果我们不熟悉数据结构和算法,那我们写的程序可能效率低,所以两者缺一不可。程序语言的学习应当放到第一位,就好像我们要先学会写字,再去学会写作,但同时也不可能说语言彻底学好了再学算法,因为语言的学习也是没有止境的,当学到基本的语法和常见的库函数知道后,就可以开始语言和算法的同步学习。
我将从语言学习、算法和数据结构学习两方面进行总结。
我通过安卓的APP C语言编辑器以及B站公开课程还有各类教科书进行C语言的学习,重点放在循环、判断、递归、指针的使用,内存机制的理解。由于没有数据结构和算法的知识,只能做些质数判断、简单运算以及小学数学应用题之类的。
这个阶段的学习让我可以用C语言实现大部分我想要完成的操作,为后面数据结构和算法的学习打下基础。具体而言,当我学习算法和数据结构时,不再需要一行一行啃代码,只需要理解核心的逻辑,就可以自己去实现。比如做N皇后和解数独时,我根本不知道回溯搜索算法,我的想法是实现任意重的循环,我就去搜索发现了一篇论文,我根据论文的思想自己写出了回溯搜索(后来才知道是这个名字)。
C++的学习是为了避免C语言大量重复造轮子,提升周赛AC速度,快捷使用各类数据结构。我使用B站清华大学的C++课程资源,以及清华大学编著的C++程序语言设计进行学习。C++STL库的使用非常方便,但是也带来了很多问题。当我们自己造轮子的时候,我们熟悉每一个细节,而当我们调用现有的库的时候,我们不够清楚实现的细节。比如set进行运算符重载operator()时必须要加const,否则就会报错(有一次周赛除了没写这个const过不了,比赛结束后加上就能AC了),迭代器访问时不能边插入(修改)边循环。解决这些问题没有特别好的办法,只能多使用、看源码、面向搜索引擎,多策并举提升熟练度。
至今,我在Leetcode上面用C语言完成了305道题目,用C++完成了780道题目,C++的使用熟练度已经超过了C语言,目前已经全面转向使用C++做题。
数据结构这门学科高度抽象,如果采取碎片化学习,做一题学一点,那么想形成体系十分困难。所以我花了一个多月学习了B站青岛大学和浙江大学的数据结构课程(为什么不学清华的呢?因为画质太远古了)。
数据结构学习的最好方式是造轮子,没有造过轮子,就不算掌握了相应的数据结构。虽然说我造轮子是被迫(当时不会其它语言没有库函数能用),但是造轮子的日子提升了我对数据结构的理解。尤其是图论这种比较复杂的结构,只有去从建图开始,到各种拓扑排序、最短路、并查集等手写一遍,才能从一知半解到基本掌握。
算法我是散装学习的,大量做题,遇到一个不会的就学一道。通过专题性刷题(学习计划拿勋章:算法入门基础进阶、二分、图论、动态规划等等)与散装刷题查漏补缺,逐渐丰满算法技能树。
这样学习的好处是兴趣高,偶尔能够凭借自己的灵光一现写出了没见过但是知名的算法,就特别使人开心。坏处也是明显的,不够系统,遇到的不会的算法可能还有不会的前置算法,前置算法不会就无法学会,最终要一路找到第一个不会的点一个一个学下来,学习困难较大。
程序语言和数据结构算法已经有了基础的掌握,接下来就是大量的算法题目训练了。通过总结前辈们的学习经验,发现有两个路线,一是不撞南墙不回头凭自己的本事死肝做出来,哪怕几个小时几天。二是15分钟想不出来就学题解,定期复习。我由于不甘心看题解,所以主要采取第一种,这么刷题的坏处很明显,慢!但是好处是扎实,刷过的题目都是自己做出来的,再通过和题解对比找差距,完善思路,刷的题目即使不复习也能够做出来。
但是对于毫无思路的题目,大约有不到5%,在长时间思考没有思路时,我还是会看题解思路,然后自己实现的。我发现这种题目需要复习,否则几天后就忘了。
我主要通过虚拟周赛和周赛来训练。只刷专题可以提升某项算法的熟练度和深度,但无法提升算法的综合运用能力。可能会出现需要的算法都会,但是想不到要用什么算法去解决。所以我通过大量虚拟周赛来提升自己灵活运用算法的能力,前后刷虚拟周赛70余场,正式周赛22场,目前周赛分数达到2070。
虚拟周赛只有当做真实周赛定时独立完成,才有训练价值,因此对于两者的经验合并到一起(统称为周赛)。周赛最关键的是找不足,而不是上分。如果运气好上分就沾沾自喜,遇到掉分场就郁郁寡欢,那么永远无法突破瓶颈。遇到不会的题目要开心,因为这说明是有效场次,找到了自己的短板。比如有几周总遇到区间处理,我就学会了线段树。所以赛后补不会的题目至关重要,力扣有每场比赛的记录,定期看一看以前不会的现在有没有又不会了,涉及的知识点有没有遗忘。
据我的经验,相对完整的数据结构和算法知识体系是上分的前提,当我基本掌握常见数据结构和算法后,最近两个月没有掉过1分(目测马上要狂掉,最近没有有效输入)。
目前,我大概有50%的概率能AK,一般可以很快AC三题,然后专心处理T4。最近10场的AK率达到60%,没有AC低于3道的场次。
由于文科毕业,读书时贪玩也是学渣(学算法是因为好玩),且已经参加工作(与编程无关),大龄没有算法基础,时间少精力差,所以学习速度慢(KMP能学一天,线段树能学一周)。
很多算法是通过刷题学会的,没有形成系统的算法思维。
刷题具有成瘾性,会挤压学习时间,导致沉溺于AC的快感而没有实质性的提高。
下一步,我计划补牢基础,学习离散数学、计算机组成原理、操作系统。提升上限,学习数论、数学分析、高等代数。系统性学习梳理算法,学习算法导论,总结已经学会的算法建立关联和层次结构。少刷水题,除非是身体不舒服或心情不好时奖励自己。
会当凌绝顶,一览众山小。后续学完算导再学习算法竞赛入门,然后去刷CF等更难的平台,争取在明年这个时候在leetcode达到2600分。