数据结构和算法(Python和C++语言描述)
上QQ阅读APP看书,第一时间看更新

1.1 概要

不论你信不信,计算机编程的第一门课里就已经涵盖了解决任何可以用计算机处理的问题所需要的所有工具了。著名的计算机科学家——艾伦·麦席森·图灵(Alan Mathison Turing)曾经提出过一个现在已被人们普遍接受的猜想:任何可以用计算机解决的问题都只需要用到所有计算机编程语言都包含的基本语句。这些基本语句也就是:条件语句(例如if)、循环语句(例如for和while)以及存储和检索数据的能力。可能你已经知道了这些,想知道还有什么其他的可以学习。这是个好问题,我在下面的内容里回答。

如果将计算机编程看成与造房子类似的过程,那么掌握了编程知识就相当于是知道了如何使用锤子、螺丝刀、锯子和钻头等这些工具。这些工具虽然可以说是建造所有的房子都需要用到的基本工具,但会用它们并不意味着你可以精确高效地建造一个适合居住的房子,更不用说建造一个满足现代建筑规范且有一定规模的房子了。这不是说你不能用这些工具做一些有用的事情,如做一个长凳或鸟笼,只是你还没有准备好应对大型项目所带来的挑战。

编程就像建造房子一样,在处理大型项目的时候是需要更多额外的知识、技术和技能的。本书能让你学习到这些额外的知识和技能,并且打下坚实基础,让你在未来的学习和整个职业生涯中可以不断提高。在你学习本书的过程中,你将会完成从“小作坊”编程慢慢转型到“大型”编程的过渡。

不同的软件项目在很多方面都有各自的特点。就规模而言,它们可以从非常小(例如将温度从摄氏度转换到华氏度)到非常大(例如计算机操作系统)。同样,软件项目在开发系统的关键任务方面也存在很大差异。一个日记网站并不需要设计得像网上银行那样严格遵守规范,也不用像控制生命维持医疗设备的程序那样精益求精。

没有任何一个单独的属性可以决定项目的“大小”或“难度”。但一般而言,有许多特征可以将现实世界里的编程和你迄今为止看到过的简单的课堂练习题区分开来。这些特征如下。

程序大小 到目前为止,你可能写过包含几百(或者几千)行代码的程序。但是,在实际生活中的应用程序拥有数十万或数百万行代码并不罕见。例如,Linux操作系统内核包含大约600万行代码。

单个程序员vs、开发团队 到目前为止,你编写的大多数程序可能都是你自己的项目。但是,现实生活中的大多数软件都是由若干个开发团队共同完成的。没有哪个程序员能够完全了解一个系统的各个方面。

从头做起vs、已有代码 可能你编写大部分程序都是从头开始的。而在实际项目中,程序设计经常会建立在已有程序之上。老系统可以和新软件一起进行扩展、被引用、被取代以及使用。

系统生命周期 当刚开始学习编程的时候,你可能会编写很多仅仅被用在作业里的程序。一旦这些程序被打了分,你可能永远都不会再看它一眼了。大多数真正的软件项目都有很长的生命周期。在它们被使用的整个时间段中,它们将会持续获得优化、改进以及更新。

环境的复杂性 一个小项目,只用单一的编程语言和一小套标准库就能完成编写;而大型项目,则更倾向于使用多种不同的编程语言和大量支持开发的工具和软件库来协同开发。

“大型”编程的基本问题其实就是管理相关事务的复杂性。遗憾的是,人们更擅长同时只跟踪少量的几件事情。因此,为了处理复杂的软件系统,我们需要一个可以在任何给定的时间节点上都能限制需要考虑的细节数量的方法。例如忽略一些细节的同时,更专注于与手头问题相关的细节。这一过程我们称之为抽象。高效的软件开发就是构建合适的抽象。因此,本书经常会用到抽象的概念。

处理复杂性的另一个重要技术是会重用之前已有的解决方案。作为程序员,你将需要学习如何通过各种应用程序编程接口(Application Programming Interface,API),来调用你将要使用的工具或库。API是代码库提供的类和方法,以及它们的使用说明(也就是参数和返回类型是什么,以及它们分别代表什么含义)的集合。你可能已经学习过一些简单的API,如Python数学模块(math)中提供的函数以及各种内置数据结构[如列表(list)和字典(dictionary)]的方法。还有一个常见的API的示例就是图形用户界面(Graphical User Interface,GUI)工具包。

大多数编程语言都提供可用于完成许多常见任务的API。当然API也会因语言和操作系统的不同而产生差异。因此这本书不能够涵盖你将会在职业生涯里遇到和使用的所有API,与之相比只是一小部分。但是,通过学习一些API,抑或更重要的是,通过学习开发自己的API,你能够获得使你将来能轻松掌握新的API的实用技能。

另一个和重用API已有的代码同样重要的能力是:能够利用现有的良好设计原则的知识。多年来,计算机科学家们已经开发出了用于解决常见问题(例如搜索和排序)的各种算法,并构建出了在大多数程序中都可以用作基本模块的各类数据集合。在本书里,你将会学习这些算法和数据结构,以便你之后可以编写更大、更复杂、精心设计和因为使用了这些被广泛理解的模块而易于维护的程序。同时,学习这些现有的算法和数据结构还将帮助你了解如何为将来可能会面临的特殊问题创建属于自己的新算法和数据结构。

计算机科学家还发明了用来分析和分类各种算法和数据结构的效率的技巧。利用这些技巧,你就可以预测一个程序是否能够在合理的时间和有限的内存等条件下成功解决问题。当然,你还需要学习算法分析的技巧,从而能够分析你新发明的算法的效率。

不论出于什么原因,拥有多种编程语言的经验是非常重要的。因此,本书将会介绍两种不同编程语言的抽象和数据结构。了解不同编程语言之间的差异,可以让你了解到开发人员可以使用不同的工具来应对不同的任务。拥有大量的工具,能够让你更轻松地解决各种各样的问题。然而,更重要的一个优点是:你还能够看到抽象、重用和分析这些基本原则是如何应用于两种不同的编程语言之中的。只有了解了这些不同的实现,你才能真正理解什么是基本原则,而不仅仅是知道了特定编程语言的细节。无论你将来使用何种语言或环境,这些基本原则都将非常有用。

说到编程语言,在本书即将出版的时候,Python发布了新的版本(Python 3.0)。新版本中包含了大量的重新设计,并且不会向下兼容2.x版本的Python编写的程序。虽然本书中的代码是用Python 2.x风格编写的,但是我们将尽可能地尝试使用与Python 3.0兼容的规范和功能。同时,将代码转换为3.0版本也非常简单,要使代码能够在Python 3.0中运行,你需要记住以下不同。

•print成为函数。你必须要在需要输出的表达式序列左右加上括号。

•input函数的使用方法将会类似于之前的raw_input。如果你需要检查用户输入,就必须显式地调用(eval(input("Enter a number:")))来获取用户输入。

•range函数将不再输出列表。你仍然可以像以前那样在for循环中使用它(例如for i in range(10):),但是你需要通过nums=list(range(10))这样的代码以显式的方式生成列表。

•单斜杠运算符(/)将始终输出浮点结果。想要输出整数的话,你可以使用双斜杠运算符(//)(Python 2.x中也同样支持)。

本书的在线资源中,提供了所有代码的Python 2.x和Python 3.0版本。因此,不论你使用哪个版本的Python,都能够很方便地学习本书。