仁厚黑暗的地母呵,愿在你怀里永安她的魂灵!— 《阿长与山海经》 鲁迅
从新视角来看待旧问题,则需要创造性思维。— 爱因斯坦
前言
我们先不看数据结构,姑且可以将数据当做一个限定词,单纯来看结构,仅以结构来看,结构是什么?结构一次出现在各个行业,比如建筑结构,人体结构,物质结构。那结构是什么:
结构是指在一个系统或者材料之中,互相关联的元素的排列、组织 --《维基百科》
直观上能被人所感受到的是就是房屋的结构,在走入一间房子,各个房间之间的排列。再进入到卧室,家具之间的排列。如果设计的好,那么这个房间通常情况下会让刚进来的人充满好感。那么如果排列摆放的不好,那么就可能让人对这个房间产生不好的感觉。比如将卧室灯的开关不放在床边,统一放在门口,这样在睡觉的时候,你就不得再下床,将灯关掉。一般的设计是门口放一一个开关,这样设计的目的是为了在进门的时候就直接能将灯打开。因为建筑面向的始终是人,所以我们总会讲宜居的建筑。
那么我们尝试在结构上面加上数据,那么就可以得出类比数据结构的定义,在一些数据之中,数据之间的排列、组织。在计算机科学中,这个数据是涵盖比较广的名词,我们以日常非常常用的ctrl+z(撤销操作)来说明,我们的一个一个操作被操作系统记录成了下面这种形式:时间为轴线,最先的操作在最下面,这样我们ctrl+z的时候就能恢复到离当前时间最近的操作。通常这种组织数据结构的形式,我们一般称之为栈(也有称之为堆栈),先进后出, 在这个场景下,操作也是数据,数据在计算机科学的含义是多种多样的,可能在其他领域数据更偏向于数字多一点。这种组织数据的形式也是符合直觉的。
我们在来看一下数据结构在维基百科中的定义:
在计算机科学中,数据结构(英语:data structure)是计算机中存储、组织数据的方式。这次的类比分析,看来我们类比的方向是正确的,这个存储我们可以理解为容器,我想起之前和我同事之前的一次下厨,他从家带来了一桶泡菜,这个桶我当时记得是那种油桶,就是口比较细,他当时让我取点酸豆角,这一度让我很痛苦,因为用筷子夹出来真的很不容易,我们可以将泡菜里的菜理解为数据结构中的数据,存储就由那个油桶来完成,组织数据的方式没有什么规律。悄悄的吐槽一下,那这个数据结构设计的就蛮不合理的,除了在运输的时候,显的稍微方便一点,但是要吃的时候是真的不方便。
数据结构简论
程序所要处理的数据
学过程序设计的都知道,用人脑驱动计算机的方式是编程。瑞士著名的计算机科学家,大名鼎鼎的Pascal之父尼古拉斯·沃斯(Niklaus Wirth)就变成的概念,提出了一个著名的公式,并由此获得了计算机科学界的最高荣誉"图灵奖",公式仅仅有一行文字:
Algorithm(算法) + Data Structures(数据结构) = Program(程序)
软件程序发展到今天,我们可以说这个公式并不是那么大的恰如其当,因为软件后面开始跟上工程了这个名词,考虑的因素更多了。编程首先要解决两个问题: 算法设计和数据结构设计(许多高级语言都内置了相当成熟的数据结构,但这并不代表学习数据结构已经失去了意义,一方面这是考点,另一方面它也确实能够帮助我们解决不少问题)。算法是处理问题的策略,而数据结构是描述问题信息的数据模型,程序则是计算机按照处理问题的策略处理问题信息的一组指令集。
我们程序设计的目的就是让计算机帮助人们自动地完成所要处理的复杂任务,计算机科学与技术的根本问题就是--什么能够被自动化以及有效的自动化? 具体的由实际的问题到最终的计算机求解,要经过怎样的过程?
我们常常要分析的就是,问题模型中信息包含的数据是什么、数据间的联系是什么、以什么样的形式存储在计算机中,分析后形成数据结构。在上世纪三四十年代,最初发明电子计算机的目的是进行科学和工程计算,其处理的对象是纯数值性的信息,通常人们把这类问题称之为数值计算(具体的说就是有效利用计算机求解数学问题近似解的方法与过程)。
近十几年来,随着计算机的快速发展,这不仅表现在计算机本身性能不断提高,更重要的是可输入到计算机中的数据,其范畴被极大扩大,比如,符号、声音、图像等多种信息都可以通过编码存储到计算机中,与此对应,计算机的处理对象也从简单的纯数值型信息发展到具有一定结构的信息,计算机的应用领域也在不断扩大。
所谓的"非数值计算"问题,是为了区分前面提到的"数值问题"而言的,非数值问题涉及的数据及数据间的相互关系(注意这里的相互关系,粗略的说我们可以认为,数据和数据之间的关系构成了数据结构),一般无法用数学公式、方程等描述。如排序问题,检索问题等。需要另外设计数据的描述方法和相应的算法来处理。
举个例子,求π的值我们就可以认为是数值运算的典型问题。关于非数值运算,让我们来看一下现实生活的一个实际场景,比如说打电话,通常现在的智能手机的通讯录都是按姓氏首字母的拼音来进行排序的,这样也能快速查找。这是事实上就是一个非数值问题,我们在查找数据,每个数据由姓名+电话构成,我们在查找的时候,查找策略就有如下几种:
- 顺序查找(一个一个查找,如果这个人的姓名的首字母排名靠前还算比较幸运,但是如果不幸排在最后,比如说姓张,如果就要查找到最后才能找到你要联系的人)
- 根据拼音首字母查找,然后在顺序查找。但是这样其实这会有些问题,如果你姓张的朋友比较多,那可能你还是要查找一段时间(一般的智能手机都支持按名字来搜索,事实上这是另一种搜索策略)
再比如我们日常司空见惯的红绿灯,我们自然会提出这么一个问题,在十字路口,要设置几种颜色的交通灯才能保持正常的顺序,这个问题我们很快就能得出答案,两种。但是如果输入到计算机中呢?让计算机来求解,就不是一个容易的问题了。首先要解决的问题是如何把题目中的信息存储到计算机中,然后在此基础上才能设计算法求解。
我们来尝试来解决一下这个问题,或者说将问题所涉及的信息建模然后输入到计算机,路口如下图:问题涉及的对象,四个路口ABCD,及相应的通路。用AC表示A到C有一条道路。某方向通行时,另外某些方向不能通行。AC-BD表示AC、BD不能同时通行。假设左拐通行规则和直行一致、右拐随时都允许。根据以上分析我们可以画出下面的路线图:这种结构我们在数据结构中一般称之为图数据结构,每个路线我们称之为一个结点。
我们再来看下计算机的文件目录,我们知道一个文件夹可以包含一个文件夹,可以这样层层包下去,在操作系统中我们也是将每个文件目录抽象成一个结点,则这个文件目录的结构图就如下所示:这在数据结构中通常被称作树,最上面的文件我们称之为根结点。
数据结构的引入
从前面的例子我们可以看出,非数值问题中的树、图等结构模型,无法用方程式等加以描述,因此解决此类问题的关键不再是分析数学和计算方法,而是先找出问题中要处理的数据及数据之间的联系、组织形式、存储方式、表示方法等,再设计出适合计算机解题的模型。
一般认为,数据结构是由数据元素依据其本身内在的逻辑组织联系组织起来的。对数据元素间的逻辑关系的描述称为数据的逻辑结构(上面我们提到的树与图就可以认为是逻辑结构);数据必须存储在计算机内,数据的存储结构是数据结构的实现形式。是计算机内的表示;除此之外,讨论一个数据结构必须同时讨论在该类型上执行的运算才有意义。一个逻辑存储结构(有的时候我们也称之为abstract data type))可以有多种存储结构,在不同的存储结构之上,数据处理的效率是不同的。
数据结构的基本概念
通过上面的讨论相信各位同学对数据结构已经有了一些基本的认识,下面我们来介绍一些数据结构的常用术语:
- 数据(Data)
在计算机科学中,数据是指所有能输入到计算机中并被计算机程序处理的符号的总称。
- 数据元素(Data Element)
数据的基本单位,也称"元素"和"结点",在计算机程序中通常作为一个整体进行考虑和处理。一个元素可以包含多个数据项。
- 数据项(Data Item)
数据项是具有独立含义的最小标识单位,是数据最基本的、不可再分的数据单位
- 数据结构
由某一数据对象及该对象中所有数据成员之间关系的组成。数据机构包含三个要素: 数据的逻辑结构、数据的存储结构、数据的运算。
数据的逻辑结构
数据的逻辑结构,反映了我们对数据含义的解释,它可以用一组数据以及这些数据之间的关系表示。数据的逻辑关系又分为以下几种类型:
- 集合
只是被放在一个容器中,像我上面提到的泡酸菜一样,菜与菜之间没有什么联系。
- 线性结构
结点间的关系是一对一的,像是排队一样,也像是的撤销操作。
- 树形结构。
结点间是一对多的关系,像上面的文件目录一样。
- 图形结构
结点间是多对多的关系,像上面的图结构一样。
数据的存储结构
数据的存储结构又称为物理结构,是数据及其逻辑结构在计算机中表示方法,指数据如何在计算机中存放,实质上是内存单元分配,在具体实现时用计算机语言中的数据类型(Data Type)。
这里的数据类型我们也可以理解为容器,对于整数我们采取一个容器,对于小数我们也单独采用一个容器。常见的数据存储方式有四类:
- 顺序存储结构
用一组连续的存储单元来依次存储数据元素,数据之间的逻辑关系由元素的存储相邻位置来体现。数组是一种常见的数据存储结构
- 链式存储结构
在每一个数据元素中增加指针项(没指针的通过引用),以记录数据元素间的逻辑关系。
- 索引存储结构
在存储结点信息的同时,建立一个附加的索引表,类似于字典中的索引项。
- 散列存储结构
散列存储方式,以结点的关键字为自变量,通过函数关系,直接计算出该结点的存储地址。
数据的运算
数据的运算有两个方面的定义,运算的定义与运算的实现,运算的定义,取决于数据的逻辑结构,知道了问题中的数据及数据间的联系,我们就可以设计相应的数据处理方法,一般常见的运算操作有如下这些:
- 初始化: 对存储结构设置初始值,或者申请存储空间
- 判空: 判断存储空间是否未存放有效值的状态
- 求长度: 统计元素个数
- 查找: 判断是否包含指定元素
- 遍历: 按某种次序访问所有的元素,每个元素只访问一次
- 取值: 获取指定元素值
- 插入: 增加指定元素
- 删除: 删除指定元素
写在最后
本篇就拉开了数据结构与算法学习的序幕,我的想法是在讲解算法题目之前,先将基础打牢。有些文字也许只有有一些阅历才能懂得一样,就像初中学习的鲁迅先生的文章《阿长与山海经》一样。有的时候,总需要经历,才能有所体会。