介绍
在备赛 xcpc 时,其实除了数据结构以外,绝大部分常用的大纲知识都学习了,但数据结构确实是练得最多的,本文主要介绍一下个人是如何学习数据结构的。
数据结构概述
数据结构大概是很多人比较抵触系统学习的东西,因为许多数据结构来说,光是板子就比其他领域的算法长很多。比如线段树,可能是很多人从数据结构新手前往数据结构殿堂遇到的一道很大阻碍。
当然了,学习数据结构也不只有数据结构题,数据结构往往可以去优化许多其他算法,比如 \(dp\) 问题,\(2-SAT\) 优化建图问题等等。
数据结构的考量
我个人觉得对于一个人的数据结构的水平体现为以下几点:
-
码力,是否有着良好的调试代码能力,是否具备所想即可正确使用代码实现的能力。
-
抽象思维,对于数据结构来说,本质上就是维护信息的东西,那么维护什么样的信息是需要你去思考的,如何把一个题抽象出数据结构,着重于分析维护信息。
-
经验,我认为学习数据结构离不开经验,是需要大量地练习。大量的练习可以让你积累许多经验,使得第一点,你可以比较清楚怎样的语法去实现是比较容易调试和分析的,第二点则你可以反应出需要设计怎样的信息:最常见的例子,我们多维护前缀最值之类的具有单调性的信息可以实现树上二分。
-
实现能力,许多人学习各类的数据结构喜欢停留在思维概念上。比如线段树的大致思想知道,但并没有去实现,对于数据结构来说,可能一个函数有各种不同的写法,有着各种不同的优势,就比如线段树的查询来说:分为了 需要临时变量合并信息/不需要临时变量合并信息,后者则可以更好地使用结构体重载运算符快速地书写复杂的信息合并。
-
知识储备,数据结构离不开知识储备,就比如很多人认为学习了线段树以后,学习 \(ST\) 表、猫树之类的数据结构是无用的。每一种数据结构都有它所擅长的优势领域,当如果你在写过大码题,显然一个更小的常数的数据结构可以使得你的代码更不容易被卡常。
-
理性思考,数据结构不同于其他算法,必须理性思考问题:\(\log^2{n}\) 与 \(\log{n} \times \log{V}\) 看似都是双 \(\log\),但它们的空间和时间常数远远不同,这个过程中,你不能想当然的大致时间复杂度而忽略二者的差异。比如树套树当中,你内层树的值域并不离散化,那么线段树套值域线段树的空间可能会远远超过 \(1G\),但离散化后可能才 \(400mb\)。
-
无他,惟手熟尔是数据结构的殿堂进步的捷径。对于初学者来说,万万不可以太过依赖于模版,否则学习更深层的数据结构当中,会出现很多疑惑点,比如线段树合并、可持久化线段树、动态开点线段树等等。当你如果能快速书写一个线段树的代码,那么你可以从代码当中体会到信息的维护过程,从而具备抽象思考的能力,当然线上赛追求速度可以考虑使用板子,学习的时候尽量多写写,因为写法不止一种,每个人有最适合自己的写法,并且写多了踩坑多了,会学习到每种题的经验。
数据结构学习的道路
学习数据结构离不开一条成熟的路线指引,对于不同需求的选手,有着不同的推荐指南。
我们需要清楚,常见的数据结构无非分为三个方向:序列上问题、树上问题、图上问题。
对于这三个方向的问题可以分别练习,并且有许许多多的技巧都是书上不会讲到的。
-
STL 容器。虽然这东西是 C++ 的,但并不妨碍它类比到其他语言。首先最常见的就是:队列、栈、堆。这三个无论如何都需要学习好它们的概念和特点,因为它们将会伴随着你的整个数据结构生涯,需要特别熟练掌握。其次,学习哈希表是很有必要的,很多人把 \(map\) 当哈希表是完全不正确的,哈希表是 \(key\) 是无序的,是存在哈希冲突的,我们常常会使用手写哈希解决,这点可以参照 oiwiki,或者使用 \(pbds\) 的哈希表,更不容易哈希冲突且更快。关于内置的平衡树,因语言不同,那么在此不详细讨论,但一些基本概念比如 \(map\) 底层是红黑树,修改和查询都带 \(\log{n}\) 是需要知道的。
-
基于基本容器的数据结构。这类既算算法,也算数据结构,常见的有单调栈、单调队列。这是必须掌握的东西,并且概念学习明白。除此之外,例如懒删除堆、对顶堆的技巧可视个人能力进行选择性学习。
-
ST 表。这个我个人认为是很有必要学习的,相信每个人都会接触到基础算法,那么一定会学习 倍增与快速幂,在基于倍增理念的基础上,学习 \(ST\) 表的成本是很低的,并且你这也将成为你数据结构历程上第一个学习到的非常有用的静态非可差分性问题的数据结构。当然了关于它的拓展:\(2\) 维 ST 表、尾部新增元素更新、分块 ST 表、四毛子算法之类的可视自身能力掌握。它也是解决 \(RMQ\) 问题的最常见手段之一,它的常数小,代码短,是属于新手学习数据结构的一个很好的 跳板。
-
并查集。这个是属于贯彻数据结构一生都必不可缺少的东西,关于它你需要把所有东西全部学完:按秩合并、启发式合并、路径压缩、如何实现带权信息合并、可撤销并查集、种类并查集(扩展域并查集)、可持久化并查集(选学)。其中可撤销并查集是你通往高级数据结构之路一个必不可缺的东西,它体现了一类经典的技巧-----回滚思想,这类思想将伴随着你遇见的问题增多而有更深的体会。
-
字典树。这个没啥好说的,是于字符串当中好学又好写的东西,同时学习 \(01\) Trie,为未来的位运算问题的题进行铺垫,关于它的可持久化属于选学,并不是很难,频率较低,其余技巧比如全局 \(+1\) 这些,属于 \(ynoi\) 类的题当中会遇到的了。
-
树状数组。这是你迈向高级数据结构的入门,你需要把这个数据结构反复学习,不断学习。关于它,你要开始正式思考数据结构维护的是哪一维信息,不同维需要进行怎样的不同处理。当然了,在这当中,你也可以学习一些比较好用的技巧,比如基于哈希表的动态开点树状数组、树状数组上倍增等等。对它的评价为:其实就是维护 动态前缀信息。
-
线段树。大概是很多人会用一辈子的数据结构。首先学习关于树的基本相关知识,了解线段树是一棵完全二叉树。其次理解线段树如何维护区间信息的,常见是如何实现的?不同的人有不同的实现风格,初学者可以先自行学习任意一种实现方式。学习基本的单修区查线段树,建议反复练习十遍左右。然后应该学习懒标记的线段树,在这个过程中,建议做做一些板子题。然后关于线段树的进阶其实并不需要太过着急,可以先从动态开点的线段树进行学习,然后线段树分裂、合并、二分这个建议系统性学习,配合题目进行练习。
-
可持久化类数据结构。关于主席树是必学的,不用想着被替换,有时候离线难写的很,这东西学习成本低,如果前几点学明白了,学这个很快。至于可持久化的其他的属于选学,学习性价比并不高,可持久化的平衡树蓝桥杯倒是考过。
-
平衡树与文艺平衡树。我更愿意把这二者分开,前者是维护关于权值类的序,后者则是下标的序。后者可以看做另一种线段树,但能做的奇怪操作比线段树更多,空间比线段树更小。关于平衡树,我绝大部分都学习过了,个人推荐学习 \(fhq\) 与 \(splay\) 即可,其余的性价比并不高。
-
笛卡尔树。如果你学习了平衡树,建议把这个还是顺带学习,就一个单调栈建树,化 \(RMQ\) 问题为树上的 \(LCA\) 问题,很方便一道堆关于 \(max/min\) 之类的区间数量统计。
-
树链剖分。重链剖分是比较有必要学习的,属于很板子的东西,学懂了序列上的,这个东西自然而然就会了。长链剖分较冷门,一般用于优化 dp 的,属于选学。虚实链剖分即为动态树。
-
动态树。学习 \(LCT\) 其实就够了,\(GBT\)、\(Top Tree\)、\(ETT\) 属于如果想要拓展学习再进行学习。动态树其实码量不大,思考起来常见的是基于容斥思考,容斥设计信息,有余力可以学习:动态 dp、动态 MST等等之类的信息。
-
点分治/点分树/树上启发式合并,做树上大规模查询问题会用到,常见的树上启发式合并比较好学,解决子树类问题,顺便建议学习一下 dfs 序的基本概念。点分治、点分树解决大规模树上路径统计,专题练习即可。
-
猫树。选学,是用来解决静态分治问题,它的理念比较像 \(zkw\) 线段树,可以选学。
-
分块。分块是很有必要学的,当你打算进阶 \(ynoi\) 的一些大数据结构题,分块是必不可少的一步,学习方式和线段树类似,但有很多额外需要学习的:值域分块、序列分块套值域分块等等。
-
莫队。个人建议必学的关于查询问题的妙手。要想学好莫队,使得自身不会只写板题,那么一定要学习可撤销的概念,学习:值域分块、回滚莫队、带修莫队、莫队二次离线。普通莫队用处较小,树上莫队其实常见的树上问题解决工具太多了,性价比也低。
-
势能 ds。这类问题比较特别需要单独练习才行,其实题目也较少,主要为洛谷的 bzoj 上的题。如果想要学习更难的势能线段树的题,可以学习吉司机线段树、KTT 之类的。
-
字符串的自动机部分,ACAM 是很有必要学的。如果是想打 xcpc 的,可以把后缀系列的学一下,pam 也可以学一下。常见的其实可以转化为树上问题
-
关于计算几何,李超树和凸包可以都掌握,半平面交可以考虑选学。前两个很有用,可以都学学,基本入门倒是不难。
-
虚树。虚树是一个比较好用的东西,其实学习难度不大,如果你有笛卡尔树的建树基础的话,应该能快速掌握,主要是应用于加快树上 dp 的。
-
树套树还是可以学学,归并树之类的都比较好用好写。
-
其余杂 ds,析合树可以学学,解决排列问题可以的。
关于数据结构平台练习
LeetCode 的话其实不太适合练习数据结构的码工,但上面的数据结构专栏有不少题比较适合新手,新手可以自行练习。洛谷上有许多模版数据结构题,可以考虑先刷熟练模版题再考虑进行其余题练习,其余题其实可以考虑罗勇军的 <<算法竞赛>> 这本书中的高级数据结构专栏课后习题进行选择练习。关于 atcoder 的话,可以把 abc 的 G 题单独拿出来,筛选数据结构的题,那些都是比较常见的应用练习。关于 cf,可以使用 ACD,在上面筛选 \(2000 \sim 2400\) 的数据结构题进行练习。
关于 cf 的 \(2800+\) 的数据结构题,大部分具有技巧性,对码工的要求并不是很高,可以单独作为知识点进行学习。对于洛谷的 \(Ynoi\) 专栏,可以选择性练习,大部分比较需要具备卡常能力以及码工。
关于数据结构还有许多一些技巧性的东西,比如离线扫描线、整体二分、cdq 分治之类的等等,都是有必要在学有余力的时候进行学习。当知识储备差不多的时候,可以考虑筛选 ccpc 各个省的省赛题中的 ds 题进行练习。当感觉能力足够的情况下,可以做做 icpc 区域赛相关的 ds 题进行挑战。
总结
数据结构的学习是比较枯燥的,需要静下心来去学,本文也只是心得分享,并未标出每个知识点需要学习的对应题,可能后续会为每个知识点挑选合适的题型进行整理,辅助各位更好地学习,数据结构中有许许多多的技巧,比如如何线段树只需要 \(O(n)\) 空间完成所有查询、如何实现正难则反、可差性转化等等,都是需要进行大量的练习和学习。
如果要学习基础的数据结构教程,可以关注我的 b站账号 Athanasy算法,基础数据结构教程将会持续更新,区别于其他教程,主要讲解代码实现与细节,还有实用性。
标签:线段,练习,学习心得,学习,关于,数据结构,可以 From: https://www.cnblogs.com/Athanasy/p/18288795