很多人比较疑惑,学数据结构有什么用,其实这个问题比较难回答,不是因为不知道怎么说,而是往深了说铺垫太长,往浅了说,又会词不达意,因此,我想等最后再说这个问题,那么首先还是可以讨论讨论学习数据结构我们到底要学什么?
数据结构学习可以从两个方面进行分类,第一是思想和代码实现。第二是,数据结构的实现和数据结构的应用。先说第一种,思想和代码实现。
数据结构也不是凭空出现的,而是仿生的,我们在数据结构中见到的大部分结构,在现实中都有映射,比如线性表,书架上的一排书其实就是一种线性表的结构,日常中的排队行为就是反应了队列这种结构,而坐电梯,穿衣服,就有栈的思想,后进先出,你先穿袜子后穿鞋,就要对应的先脱鞋后脱袜子,这种套娃式的结构,其实就是栈。那么这些凝聚了结构的特点,而与代码无关的概念,我们就称其为数据结构的思想。或者准确的说,是其特点,如果看过数据结构教材的人知道数据结构有个ADT(抽象数据类型)的概念,甚至还用到了数学语言去描述一个数据结构,这其实都是数据结构思想的一种体现,而学习数据结构的第一步也是很重要的一步就是去理解数据结构的特点,一个栈为什么叫做栈,它和别的数据结构有什么不同,它有什么行为特点。这就是我们学习的第一步,很多同学在学习中都是有意识或无意识的以这种顺序在进行学习的,通常来说,思想比代码更容易理解一些,掌握思想的一个很典型验证标志就是会做题,但不会写代码。数据结构的考试除了代码题,也有很多计算题,比如哈夫曼树的计算,最小生成树,或者让你写出线性表的插入过程等等。它不会考察你具体代码,但是会考察你每一步操作后,数据结构产生的变化。所以学习的第一步我们说是去理解其思想和特点,可以不会写代码,但至少要先会做题。
第二部分就到了代码实现的部分,我发现很多同学都卡在这个地方,像我自己,在上学学数据结构的时候就深刻感受到了这个问题,有些数据结构确实代码难以理解,因为数据结构的实现为了保证其基础操作的效率,通常都有很多牛人在背后不断地优化和改良,最终形成的版本,概念和实现都已经比较复杂,比如平衡二叉树系列的代码。但是刚开始的线性结构,代码通常都比较简单,之所以觉得难以理解大部分是因为编程语言的能力不足,轮子都没造好,就要跑,当然会觉得吃力。这其实跟课程安排也有关系,大部分数据结构都会在大一下学期或大二上学期开设,而这个时候C语言等课程刚上完没多久,代码还没写明白,就来了一个硬核课程,确实有点坑。哪有什么办法呢,如果你无力反抗,不如默默承受。
关于怎么解决这个问题,我会在后边的文章里慢慢讲解,这一篇主要是讨论数据结构学什么。
接下来我们将讨论第二种分类,这实际上是数据构更重要的地方,很多问学习数据结构有什么用的是因为没有意识到这个问题,而仅仅将精力放在了第一种分类方式上,那么我们讲对这个问题进行一些讨论。
先说第一个概念,数据结构的实现。
上边也说了,数据结构既然已经提出了指导思想,那么代码实现就是自然而然的事,实现是实现什么?很多同学在学习数据结构的时候,对整个流程并不理解,不知道从哪开始,也不知道干什么,其实就是忽略了一个指导概念,叫ADT,抽象数据类型,很多老师也没有认真讲这个问题,自然学生也不会注意这个问题。而ADT确是连接思想和代码一个很重要的部分,关于ADT的概念我想单独拿出来一章说说我的理解,这里我们就跳过这一部分,而从整体上说明。在严蔚敏的《数据结构》里,都会给出ADT的概念,其中有一个地方要特别注意,就是叫做基础操作。那么问题就来了,什么叫基础操作呢?为什么会有基础操作这种东西?
其实基础操作对应现实来说,就是这个东西(数据结构)有什么用,比如我们说锤子可以用来砸东西,剪刀可以用来剪东西,那么砸和剪就是锤子和剪刀的基础操作,但数据结构的核心还是维持数据结构之间的关系,在保证这种数据关系的稳定性的前提下,提供初始化增删改查功能,基本上就是数据结构的基础操作的定义了。但是你说,这些操作是标准的吗,也不是,而一种实践性正确。这些操作通常来说满足了数据结构需要的操作,比如我们说的基础的增删改查,就拿线性表的例子,假如我定义一种操作,能够按顺序访问偶数位置上的元素,可不可以,可以,但是没必要。基础操作(简单理解为函数)更多时候不是做到无以复加,而是无以复减。而ADT通常就是定义了一种数据结构所需要的基本操作
用一种方式去看,ADT其实是一种数据结构的标准,就像你造汽车轮子,造芯片,造螺丝钉一样,造插座一样,它必须要满足一些特性来供上层或者说别人使用,别人不需要知道你怎么实现的,但是他知道你肯定会有这些东西,这是一种模块化的思想。我们说用代码实现数据结构,其实就是在进行标准化的操作,你可以按照你自己的想法去写代码,但是最后,功能必须符合ADT的要求。下图是从严蔚敏《数据结构》里截取的ADT片段
有一个概念很重要但是这里没法展开细讲就是一个封装的概念,模块化的概念。在c语言里,我们会把数据结构实现为一个文件,里边包含一组函数,可以对特定的结构体进行操作,反回特定的结果,而在诸如C++,java或C#这种面向对象的语言里,我们将其实现为一个类,核心都是为了方便他人调用,这一部分的概念需要在编程生涯里慢慢去理解,现在简单点说就是这样,以C语言为例,就是根据概念创建结构体,然后编写这些基础操作所代表的函数。
那么实现这些基础操作,需要具体怎么做呢,其实一般数据结构教材里都有讲,而基础操作说明上也写了功能了,通常如果C语言比较好,看到这个功能需求,大体一想,就知道怎么做了,即使不知道,教材里,也提供了细节,比如线性表的删除元素操作,它就明确讲了,需要将删除元素位置之后的元素依次向前移动,并且告诉你了删除的顺序以及为什么这样做。大部分基础操作都是在做这件事,比如你说,那我不移动元素行不行?你不移动,那么线性表元素紧密相连的特性就不存在了,所以说基础操作之所以做那么多工作都是在维持数据之间关系仍然符合它的特点。但我说的是简单的数据结构,像平衡二叉树这种实现较为复杂的结构,还是要去看具体是怎么样去写这个代码的,心中有数以后,再去自己写。
总结起来代码实现的学习就是:一抄二临三默写,跟练字差不多,刚开始不会就找一些别人验证过的代码抄,后来自己边写边参考,最后不看别人的代码,自己能把基础操作实现就算通关了
那你说我实现完了,这玩意有人用吗?还真没有。你自己包的饺子别人也不一定会吃,但是学习的阶段这个是不可少的,但是你了解标准,也知道了数据结构的特点,知道了操作的效率,对你吃饺子是肯定有帮助的。也许你以后搞科研可以成为一名科学家,去创造一种新的数据结构,但大部分时候,作为一个码农,我们更多的时候是在用数据结构,学习实现是为了帮助我们更好的使用,这就引出了第二部分,数据结构的使用。
好吧,我收回我说的话,了解思想的第一部分就是会做题,但是我思想理解了,题还是不会做啊?那你看看你是不是掉入了这个陷阱里,数据结构的实现题和应用题?数据结构的实现跟数学有一些相似之处,你学了基本原理,但仍然不一定会做一些题,这很正常,这需要不停地练习,而且还有另一个“算法”课程你还没学呢?程序=数据结构+算法,算法是什么,咱不去讲,但是这个区别我们还是多少要了解一下。你学了线性表,却不会写约瑟夫环,你学了栈,表达式计算仍然很难。这很正常,数据结构的实现像是内功,把内功转化为输出,仍然需要武功或者说招式。不然就是肉搏了。
数据结构的应用是个大块的难题,但是数据结构的课程核心还是围绕数据结构的实现来进行的,其实作为一个码农,或者以后仍然从事计算机行业的,都会不停地和数据结构打交到,而学习阶段就是打好内功,像很多高级语言都提供了基础数据结构的实现,但是你要不了解,你也不会用。学习的时候就还是脚踏实地的去自己动手实现一下每种数据结构,在应用的时候去用系统提供的现成的而不自己写,将精力转化到解决问题上,这其实也是数据结构的初衷,也是后来“框架,架构”概念的一些目的,让程序员工程师能够不断提高思维的层次,站在更高的角度上去思考和讨论问题。
还有很多问题没有说明白,但展开来确实太多,有机会,每一个小点都单独拿出来再讨论,关于数据结构学什么,你了解些了吗?