给数据结构初学者:跨过算法和程序之间的鸿沟
【摘要】学习数据结构时,将各种基本操作通过程序实现,可以加深对算法的理解,也是提高编程能力的一种有效手段。针对初学者在搭建算法和程序之间联系困难的问题,本文以线性表部分为例,介绍了如何从读算法中找出实现程序的线索,围绕算法和程序之间的联系、抽象的描述和具体的实现之间的关系,引导读者学到抽象算法的精髓,最后对实践的路线、方案等进行了总结,并给出一些建议。
【正文】
计算机是算法的科学。学习IT的童鞋,在算法中下多大的功夫都不为过。在学习《数据结构》课程的时候,将教材中给出的算法用程序代码描述出来,在实现的过程中,可以不断加深思考;在调试程序的过程中,对算法的细节能够进行精细的钻研,这些都是获得算法精髓的方法。算法往往用“伪代码”的形式给出,学生在学习过程中,将这种抽象的描述与能够执行的具体形态的代码之间建立联系,使得算法形象起来,这样一个学习过程,以及由此带来的体验,将会使学生在技术成长之路上受益菲浅。
在我组织的“未来IT工程师协会/CSDN高校俱乐部”的活动中,结合同学们正在“算法与数据结构”课程,创办“算法达人修炼营”,组成合作学习团体,实践相关的各种算法,讨论在算法学习中遇到的问题,以此来提高驾驭算法的能力。为帮助同学们做好抽象的数据结构、算法与某种语言编写的程序之间的过渡,特撰写此文。
结合我校大二同学已经有的知识结构,本文以严蔚敏老师的《数据结构(C语言版)》为基础说数据结构和算法,实现算法的语言用C和C++。(建议:读本文中,一边翻着教材才有感觉。)
一、读算法中找出实现程序的线索
要看懂算法,解决其中存在的障碍,需要同学们在读书时能够做到前后对照。
以P23中的算法2.3为例讲读算法的方法,以及如何前后对照。
算法2.3的顺序存储的线性表的初始化问题,伪代码是:

为便于后续的说明,为算法加些行号:
算法中的逻辑非常简单,常有同学说,算法是能看懂。这得益于抽象(后面专门要说),使我们忽略了很多实现中要考虑的细节,所以容易看懂,这是抽象的好处。而恰好由于忽略了实现细节中的具体形态,使得在考虑如何实现算法时出现障碍。这不是一个大问题,却成为初学者起步的一个障碍,尤其是对程序设计的功底并不很深的同学。(程序设计功底的加强是必需的,但已经到了这个阶段,并不是一定要先补上那一课再能学数据结构,时候不等人。实际上,学数据结构,同时也促程序设计。)
障碍主要来自于,算法描述中出现的“词汇”和曾经编程中用过的似乎并不相同。“字”都不认识,谈何理解,又何谈实现。实际上,会看书的同学应该发现,算法中出现的“词”,在教材前面都曾经出现过,我们找出来,将其联系到一起。
说有些同学不会看书可能委屈,更多的是没有耐心,一门课程起步阶段,基础性的内容要看细了。
算法第1行:Status InitList_sq(SqList &L)
InitList_sq是函数名自不用说。Status 显然是函数InitList_sq()的返回值类型,但究竟是什么类型呢?C和C++中没有这种数据类型,其他语言中也没有,可以猜到是自定义类型。教材P10有解释:

教材接着给出了在C语言实现算法时的建议:
SqList结构体包括有三个数据成员,在函数中都会用到。Length和listsize成员的类型是整型int好理解,ElemType又是个什么类型?理解了前面Status抽象的意义后,可以猜到ElemType又是个抽象数据类型,对应的是顺序表中要存储的数据的类型。ElemType(见名知义,元素类型)在教材前面出现过,但放在不同应用背景下,可以给出不同的定义。这个数据可以是简单的整型(若干整数的序列构成一个线性表),也可以是浮点型,甚至ElemType是一个字符串、结构体。例如,可以是:
上面的程序要用C++(或Java)实现时,将SqList定义为一个类,基本操作为成员函数(或称为方法)。
二、理解算法和程序之间的“跨度”
下面罗列网上收集来的几组说法,适当修改、补充,提供给读者从多个角度分别体会:
说法1:
算法是解决问题的步骤;程序是算法的代码实现。
算法要依靠程序来完成功能;程序需要算法作为灵魂。
说法2:
算法与程序:(1)一个程序不一定满足有穷性,而算法需要在有限步骤内完成。(2)程序中的指令必须是机器可执行的,而算法中的指令则无此限制。(3)算法代表了对问题的解,而程序则是算法在计算机上的特定的实现。一个算法若用程序设计语言来描述,则它就是一个程序。
说法3:
从计算机的角度讲,程序是用一种计算机能理解并执行的计算机语言描述解决问题的方法步骤。算法是解决问题的方法步骤(不一定要让计算机能理解并执行)。程序设计是分析解决问题的方法步骤,并将其记录下来的过程,其关键就是将算法描述出来。
数据结构课程里面的代码,都是伪代码,也就是说,用C编译器编译是通不过的,还要做很多的修改才可以。算法是编程的核心,算法出来了,我们就可以考虑用哪种语言实现比较简单,不一定要选C。学数据结构学的是算法思想,学会如何去解决问题,这才是最重要的,用C实现次之。在数据结构C语言版里面,我们只是将这种数据结构的操作用伪C代码描述出来而已。而在实现时,再考虑语言细节,将算法用计算机可以接受的方式重新描述即可。
说法4:
算法和程序都是指令的有限序列 ,但是:程序是算法,而算法不一定是程序。
算法和程序的区别主要在于:
(1) 在语言描述上,程序必须是用规定的程序设计语言来写,而算法很随意;
(2) 在执行时间上,算法所描述的步骤一定是有限的,而程序可以无限地执行下去。
所以: 程序 = 数据结构 + 算法(这是面向过程程序设计的概念,在面向对象程序设计的语境下需要拓展。)
三、理解算法与数据结构中的抽象
首先谈一下计算科学中的抽象。
抽象(Abstraction)是简化复杂的现实问题的途径,可以忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。运用抽象,可以使我们的注意力集中到要解决问题的主要矛盾上来,主要矛盾解决了,再解决次要矛盾。例如,在用计算机解决问题时,先设计抽象的数据结构和算法,再考虑如何用程序设计语言表达出来。除了降低难度,还利于保证正确性、可靠性,也便于分工,便于工程组织。
抽象是计算机科学与技术中最基本的思维模式之一。抽象包括两个方面,一是过程抽象,二是数据抽象。它侧重于相关的细节和忽略不相关的细节。抽象作为识别基本行为和消除不相关的和繁琐的细节的过程,允许设计师专注于解决一个问题的考虑有关细节而不考虑不相关的较低级别的细节。
在学习数据结构时,要能看出抽象程序的高低。举一个例子,本文前面举例用的是P23的算法2.3,而不是p20的算法2.1,我为什么这样安排?
线性表可以采取顺序表示和链式表示两种方法进行实现。算法2.3对线性表的初始化针对的是顺序表示。在链式表示时,针对初始化的操作,另有算法。算法2.3是和具体实现相关的算法。而算法2.1所涉及的两个线性表的合并,却是和采用的存储方式、数据结构的实现方法无关的,适合于上面提及的各种存储结构。从这个意义上讲,算法2.3的抽象程序比算法2.1的低,考虑细节,考虑实现更多。
于是引出一个建议。在将教材中的算法转换为程序的过程中,不必依着教材的顺序实现,而是应该先实现抽象程度低的,再考虑抽象程度高的。算法2.1的描述中,调用了不少其他的基本操作,从这一点也可以看出其抽象程度高,实现的细节都“隐藏”到其他操作中了。当然在实现和测试时,这样的算法不应该优先安排。
四、用编程语言实现算法的小结
下面的思维导图给出了将算法写成程序的要点:

五、实践指导
以“第2章 线性表”为例,P19已经给出了抽象数据类型线性表的定义(ADT List)。无论采取什么样的数据结构,其中的基本操作都是要实现的。换句话说,在今后进行项目开发时,只要涉及线性表,用到的就是这样一组算法。在学习期间,建立起自己的算法库,还是一件非常有意义的事。就此,可以安排出如下的实践内容:
(1)用顺序表示作为线性表的实现方式,实现所有的基本操作
(2)用线性链表作为线性表的实现方式,实现所有的基本操作
(3)用循环链表作为线性表的实现方式,实现所有的基本操作
(4)用双向链表作为线性表的实现方式,实现所有的基本操作
(5)项目实训:用采用顺序表或链表为存储结构,
这些工作貌似不少,真正进入,你会发现这是件很舒服的事。我课外学习的安排中,学会“自己给自己布置作业”,这是一种非常强大的能力,低年级多做些题目和验证性的实践,高年级时设计项目去做会更得心应手。
六、结语
本文谈的是如何解决学习中一个环节的问题,这或许能够帮助同学们安排好实践学习,理解、实践相结合,从而保证学习效果。但本文的一大罪是,我粗暴地揭开了算法抽象之美的面纱,为此我深感不安。学习算法,要能体会到这种抽象之美,要能到达抽象的这种层次,要深入到复杂性这个核心中来,然后具备根据工程中的实际限制在多种可选的设计方案(要考虑设备、性能要求、经费、语言、开发人员、开发周期等很多因素)中折衷的真功夫,最好还能针对已经算法存在的不适做出改进。成为高手之后,会明白选用合适的语言实现算法的问题,是一个不是问题的问题。然而,这毕竟是初学者达到更高境界必须经过的路子,能帮助初学者打通这个通道有所启发,也就为我的粗暴稍感安心了。
期待同学们更大的进步。
- 2楼wfzczangpeng昨天 19:56
- 还是老师一下戳破窗户纸更痛快啊。算法是灵魂,各种语言是皮囊,穿上哪个皮囊就变成那个语言的‘程序人’了。。。
- Re: sxhelijian昨天 21:07
- 回复wfzczangpengn其实让你们自己戳破窗户纸更好,这次的越俎代庖,期待你们更快的成长,不再需要我这个爱叨叨的人。戳破窗户纸,罪过,阿弥托佛!
- 1楼Pick_a_hole_in昨天 11:33
- 一个算法若用程序设计语言来描述,则它就是功能函数(或方法)n老师,我觉得这样说更好一点。嘿嘿···菜鸟的拙见。n谢谢您,我认真地看完了您的博文,虽然有些地方不太懂,不过确实让我数据结构学习之路清晰了很多!n顶一个啊···(*^__^*) 嘻嘻……
- Re: sxhelijian昨天 11:47
- 回复Pick_a_hole_inn很好的补充,人多力量大。