数据结构是什么?它是组织内存中对象或基本类型数值(primtive types)的形式,为了更好地组织和使用这些对象而慢慢发展起来的固有形式,惯用法(idioms),是计算机开发领域用处理数据的方法来解决问题的一套科学.
对数据结构知识的系统整理最初源于一个科学者的一本书1.
以上是常见于一些教科书上对数据结构与算法的定义。 所有这些,是针对数据结构已经被具化了,跟语言结合之后的概念,它其实首先是作为解决平台,问题,语言问题的工程模式中的一种模式而存在。所以,这些概念并没有从它的本质上去说明未必太流于浅层,。比如,它使初学者不能把握一些明显的东西。比如,为什么要提出数据结构与算法?可以不提出这样一个东西吗?它处于开发的什么层次?
它其实是用数学的眼光来解决问题的一种模式,这才是谈到数据结构时首先要谈到的话题。
这其实涉及到计算机的产生和计算机软件的产生这个话题。
数学与计算机科学的关系可以比喻为母子关系,计算机的产生源于19世纪时的一次数学危机,当时,人们发现数学不能完全表达为一些”形式”,换言之,数学是不能被形式化的,它还具有某些感性的部分,这个历史是这样的:希尔伯特为了发展欧式几何公理体系的严密性(大约从中学开始,我们就已经见识到了欧式几何的全部,值得注意的是,欧几里德在公元前300年前,就整个地完成了这里面全部的成果),而定义了一套新的”公理体系.这个公理体系就被叫做希尔伯特公理体”.它企图用一套纯粹形式化,不具有逻辑关系的推理体系来表达整个数学.它假设,任何数学课题,都可以用某一套形式化的东西给推导出来.这些推导过程只需要是根据已有的公理,进行纯形式上的复合(而不需要联系关乎它们的相关数学逻辑)得出的新的公理.无疑,这样的计划是具有非凡的意义的.这个体系对后来数学的展开的确起了很大的作用,产生了一些好的成果,只是,人们也无法确定这个工作本身是不是足够严密的.后来出现了一个叫康托尔的人,它运用泛集合论的方法,”造性地将一一对应和对角线方法运用到无穷集合理论的建立当中”,康托尔的工作,对后来者的工作起了一个承上启下的作用.源于康氏的工作,哥德尔后来提出了”不完备性理论”,这个理论结果直接推翻了希尔伯特的体系,只是希尔伯特公理体系已经产生了非常好的数学成果,人们一时陷于恐慌,这就给整个数学造成了一次”危机”,这就是被称为第三次数学危机的事情.但是危机归危机,形式化数学的工作还是在继续,为了不走进哥德尔的陷阱,只需使”算法”(形式化了的数学步骤)努力避开哥德尔专门设置的陷阱和假设就可以了,身为数学家和破码专家的图灵,后来就从数学上”建立了计算机最初的工作原理”的成果(值得注意的是,它是数学意义上的,所以是”计算机科学”),冯诺伊曼完善了它,最终从物理上造出了这样一台机器.图灵就是计算机数学之父(计算机科学之父),而冯氏,是计算机器之父.其实在那个时候,与图灵一道,另一个叫丘奇的人,也提出一套lisp数学体系(它也可以被用来制造计算机,后来的确也产生了lisp机),这二大体系,它们的区别应该首先是体现在数学上,然后才体系在其各自对计算机的意义上(图灵的抽象机更易产生出物理的机器,而丘奇的太过于注重数学上的美感.)那么计算机能执行的算法会是什么样的呢?首先,它必须不能是哥德尔式的.不能有缪论的发生,对于这种算法要被形式化的要求,它一定能具有有限的步骤,即复杂性理论要保证在多项式内完成,对于某个输入,一定要产生某个结果,一定要使计算机在有限步之后停机.所有这些,都是科学上的事情了..这篇文章,只是让你能对当时的历史有所理解,并体会到,在纯科学上,计算机这个东西,是吸着数学的乳汁长大的.
所以,代表计算机科学的,不是计算机编程科学,,而是与数学密切联系处的算法科学,即实现计算机的科学,宏大的《the art of computer programming》讲述的全是算法和数学相关的理论.编程部分比例几乎没有.
于是,当用数学,和机器的眼光直面解决问题的时候(而不是语言映射手段,那处在开发层次,而不是实现层次,只是语言映射也可以被用来实现而已,即系统编程语言和系统编程),很自然地就产生了一门学科,即数据结构和算法。
数据结构属于解决平台,问题,语言三种模式中的一种,即实现模式。
我们在前言中说到,数据结构与语言映射虽然都可以被于编程的形式体现,然而,数据结构是解决问题的问题,而语言映射是解决映射问题的问题。算法提供了一种用语言来进行设计的手段,是实现抽象,当你不知道如何实现一个程序时,先找到数据结构,自然就找到了算法,编码之后程序就实现了(数据结构的意义在这),就是这个道理,所以说数据结构和算法是通用的语言用来进行设计的抽象惯用法.
为什么说数据结构是实现模式呢?因为它用内存的方法(而内存是冯氏开发的基础东西)来直接解决问题,而不是复用的方法(内存的方法是从0到有的方法,而复用的方法是从有到有的方法,所以二者的根本本质不一样.),比如设计模式这种其它的,基于设计等的手段,或更高级抽象.而设计模式是设计上的复用方法,来解决计算机通过编程能解决的问题(比如,它还甚至直接利用抽象了的数据结构)
我们在前面谈到,编程无非有二种,一种是实现,一种是封装和复用
Dos程序员是幸福的,因为他们只需要了解有限的东西.他们不需要懂灵活的抽象,而只需要懂固定的细节.
在C+DOS下(更确切地说是在过程语言中)用过程语言开发程序的时候,一般都是先考虑这个程序会用到什么数据,要用什么样的数据结构在程序内部去表示它,然后才考虑开始写代码,,这样的编程工作也就是数据结构加算法的方式,适用于开发底层逻辑.
虽然C也可用来开发应用,但用C实现从C能表现的那些机器逻辑到应用逻辑的维度变换处处受制于对数据结构和算法的制约(我们知道运行时本身就是一个大数据结构,用C编程必须涉及到,而且OS实现中,因为用C实现,所以也存在很多用数据结构加算法实现的C的接口,要利用这些接口进行系统编程,必须了解数构和算法知识)
C语言不单要求严格的类型和语法利于算法和数据结构的建设,而且提出了跟底层直接相关的指针机制,这种用内存地址来指代机器 并表现系统问题的机制,深克地反映在用C实现的字符串,数组,这些东西上,一旦用C编程,,你就需要站在C+系统底层的立场上设计现实问题,,而显然,用C来表现系统是最佳的..
也因此,人们说算法和数据结构是数学,计算机软件,计算机硬件三门学科之间的交叉学科。
所以,算法是一种独立的科学,可以独立理解为“广泛的编程问题的具体解法”,由于关于算法的书只是整理了通用解法(这一章也是如此),更多的具体算法和解法出现在具体问题中,那才是我们编程处理特殊问题时要广泛涉及到的。
实际上,数据结构与算法解决的问题是整个编程中最有限,最底层的那些问题,(它没有涉及到设计,用户等编程三层架构中的最重要的后二层),它只解决,对于计算机(注意这五个字)在组织内存,支持对这些内存中数据进行操作(排序,查找)等有限问题的问题2,它仅仅能很好解决这些问题,所以说它是面向计算机的功能性方案3,是计算机的科学,解决对于计算机来说最为迫切要解决的那些问题,比如效益敏感类问题.
如果放在整个大编程中被讨论,那么它是颇为有限的那类东西.
算法分一般算法和通用算法,我们在这里讲的数据结构与算法,数据结构是主体,通用算法是附属(仅是查找,排序等),但在一般算法体系统中,具体算法是主体,数据结构是附体。泛义的算法二字是指代一种数学概念,一种用科学(特别是数学)来解决问题的方法。
你也可以这样理解,算法是针对数据结构的,如果说数据结构是计算机的结构,那么算法就是用来解决计算机问题的(数据结构和算法是计算机的而不是编程语言的科学,这个说法就来源于这里).
从开发(者)的观点,我们可以把OS看成是提供API的软件抽象机制,同样从计算机开发领域的角度看,我们可以把数据结构看成是算法的附属,,因为数据结构是源于算法的,而数据结构是开发中的数据抽象,,,,因此作为从开发眼光来看的数据结构是算法的附属。
数据结构与算法是属于计算机4的,而不是程序设计语言的(因为它5在纯数学上也成立6).更多在出现在计算机系统实现上.比如操作系统实现,编译系统,计算机图形上.是一种实现模式.
一切语言机制都是为了抽象,,,抽象真的有那么重要吗?? 对数据的抽象必要吗,,,
什么是真正的数据,,什么是抽象了的数据,,,,数据类型就是数据的模板,,计算机和语言用了什么样的形式语言的形式去指代和控制他们?
如果说数据结构是非语言级的设计抽象(它也是一种实现相关的设计抽象),那么高级语法机制就是语言相关的设计抽象(是一种代码相关的设计抽象),而我文章中最后一部分谈到的设计就是工程相关的设计抽象(相对实现相关,代码相关,这似乎可以称为结合了二者的综合的设计抽象了.)..
算法不是设计,(算法更多的不是代码逻辑的设计,用最小内核的流程控制比如C都可以实现算法跟数据结构)。
算法并非代码逻辑,而只是附属于语言和数据结构学交界的那些东西(算法是从属数据结构的,, 数据结构与算法问题比如排序等是紧密相连的,相互支持的),只有设计模式才是代码逻辑和代码抽象学。
我们在前面谈到了类型与数据,那时我们就谈到,语言的类型机制为计算机运行更抽象的数据提供了基础,那么这里就是开始介绍这种抽象数据的地方了
数据结构的“数据”可指计算机的简单类型,或抽象了数据类型(因此数据结构的准确含义是“计算机开发领域中的数据抽象学”),汇编不需要变量是因为程序员包揽了内存分配,而高级语言提供类型抽象,和变量,变量的内存分配由编译器或运行时完成,因此可在这个基础上发展基于靠近人的抽象数据结构,而OO既是对数据的一种抽象(当然,它跟数据结构对数据的抽象是站在不同角度的),也是一种对代码的抽象
首先,数据结构在type的基础上进行抽象,,它看不到汇编的位,只是考虑如何将type翻来复去地变换形式进行使用,而算法,看不到汇编的指令,只是考虑如何用高级语言的语句来操作这些数据结构,“即算法是对如何操作数据结构的抽象”因此数据结构和其上的操作称为adt,
而算法和数据类型是建立在type和施加在type上操作的更高级抽象..是语言,大凡具有类型机制的高级语言通用来的,用来设计应用和解决问题的方法..
数据类型Type是高级语言对汇编直接操作位的“位”的抽象,,而这句话中的“操作”,,也被高级语言抽象为施加在这些类型上的操作,比如,对于整型数,有相加,但是不能对整型数执行浮点数的运算,虽然程序员方面是直接使用Type,但在编译器方面,其实对应的还是位,编译器为程序员隐藏了内部逻辑.使我们的编程工作能维持在使用类型而不是机器语言“位”的高阶抽象层次.
一切都是抽象,数据类型的提出本身就是一种抽象,而至于提出什么样简单类型也是一种抽象,数组,记录类型,联合(要说有,其实还有递归类型),都是C的复合类型,实际上C只有int,float,char这三种类型,高级语言提出这三种类型是因为它首先是对机器类型的一种封装,这三种类型抽象了我们的现实事物.比如int,float对应数值意义的抽象,char对应字符意义的抽象..
类型机制几乎是从讲解一门语言的语义就开始涉及了,这导致了在使用此语言的一个实现时进程编程时,,如果没有弱类型机制(我们知道,强类型机制几乎是保证系统编程质量的要求之一,相比之下动态类型太偏向程序员而远离了需要严格控制类型的底层),那么我们编程处处就得涉及类型机制,变量,直到对数据结构的掌握..
语言内置类型一般包括在语言的运行时中,是该语言源程序代码跟平台(OS,CPU)的接口,,运行时驱动该语言代码映射到此平台逻辑对系统编程,,就是用”平台能干什么事情”..来构造应用…一个一开始就学JAVA而不了解低层的人,不可能深克知道如何从低层驱动计算机构成逻
一句话,type和type上的操作是抽象汇编的,,那么,数据结构和其上的操作是高级语言站在type和type操作上的抽象,一者是高级语言面向汇编,一者是高级语言面向高级语言的type.
函数是这里最能体现这种抽象的机制,首先,函数接纳实参或数据结构这些抽象,,函数体内的代码是“施加在实参上的操作的整合体”这样一种抽象..
理解数据结构前理解什么是数据和数据节点,以及它们的属性,这是很重要的,但很多人忽略了它,以至于有些教科书都混用Ordered与Sorted,实际上,对这两个概念的理解至关重要,对它们的澄清工作是一本数据结构方面的书最应该首先完成的.它直接关系着数据结构学中诸多基础概念的导出.那么什么是数据和数据结点呢,数据节点又是体现了什么样的重要属性呢?这些属性(比如刚谈到的ordered,sorted)影响了这些逻辑数据结构在以后被拿来讨论时的哪些方方面面呢?
数据结构绝非仅仅数值数据结构.数据结构不仅用来研究数值,节点数据还可以是任何类型type.或ADT(从拥有类型的语言眼光来看,一切用代码结构能编码的计算机数据都可以是节点数据),数据结构研究的数据是施加了一定限制的数据而绝非广义的数据,这主要体现在数据的二个属性上1.存不存在order还是sorted,2.有没有key value的分别,还是根本就无所谓这二种属性.
首先,数据结构能处理的数据节点是逻辑上分存不存在order的,如果无所谓order,那么集合就是这样一种逻辑数据结构,如果有order,那么就可以是线性List,层次tree(我们呆会会谈到order为何跟tree是有紧密联系的)这样的逻辑数据结构,
另外,计算机能处理的数据节点是分存不存在Key Value区别的,如果无所谓key value,那么其中每个节点都是数据实体,如果存在是key还是value的区别,那么这种逻辑数据结构中的数据可以表达为一个key,可能是一种关联式的逻辑数据结构.这就是计算机能处理的“数据‘和它能形成数据结构学这样的科学所加的二个属性,下面我们详细阐述之.
Ordered表示一个接一个,顺序不可改变,如果被改变(比如一个元素后接的元素不再是以前那个了),就不再是以前那个ordered List了,但它依然是ordered的(它的意义在于这里).所以如果一个unosorted表被sorted过了,它肯定不再是以前那个ordered List了,只是因为它当中有些元素的ordered位置换动了,所以称它是一个新的ordered且sorted的表.
不妨把Ordered严格称为循序(而不称为顺序),而把Sorted称为排序,那么我们就从字眼上对这二者进行了很好的区分.(当然你可以这么做,但为了统一起见,下文一律用顺序代替循序的说法.)
什么是key value呢?键可以是一个记录中的某个字段,或字段的组合,单字段的以这个字段值作为整个键..关键字相当于字典中的单词,,而数据项相当于这个词的词义,造句,等全部的词条信息,这样说你一定不会明白,说实话我第一次看到这样的说话也没能明白..实际上就是说,我们要查的是关于某个词的词条义,音等,,但我们是通过某个单词找到该单词的词条义,音的..在字典中单词和其音义是一同被存入字典的,都是(某记录的)数据项,但单词是作为键的数据项.
明白了以上这样我们就可以理解一种特别的数据结构了,拿典型的hash table来说吧,首先就其key value属性来说,如果存在key value的区别,就是hash set,hash map之类的东西(因为hash set,hash map是关联式的数据结构),,这跟属性中的第二点有关…第二,它的hash,是针对数据结构中的数据来说的,hash一定要是hash个数据结构出来,而hash数据结构中的数据是无所谓ordered还是不ordered的(hash table,map,set它们根本就是uniform的),而一般数据结构是非uniform的,这满足第一个属性.
研究数据结构我们目的从来不是为了得到那些底层的东西,基础的数据结构,,而是为了得到更抽象的东西,比如树,图,甚至是树图之上的,优先队列,高级优先队列,集合,多重集,区间,等.
但是抽象总是一步一步来的,在数据结构的讨论中,我们总是从最普通的情况谈到施加了各件条件和限制的情况,比如有向图是无向图的一种特殊情况(施加了有向这个条件,而无向图是一种更一般的图,因此图论中一般是谈无向图及遍历等操作,再谈有向图及其特性.最后特别是有向无环图DAG).
逻辑上,数据结构的最高境界是集合7,所以通常用集合来形式上定义其它的逻辑数据结构.数据结构包括数据元素本身和数据元素之间的关系这二方面.所以讨论时通常用数据元素构成的集合和数据元素之间的关系构成的集合为基础来定义和导出其它的逻辑数据结构.
真正的数据结构其实只有二种,表和树,因为按第一点属性来看,前者是线性order序,后者是层次order序(我认为只有这二者才是划分和形成概念的标准),如果说硬有第三种,那就是无所谓order序的,即集合,(传统中一般把逻辑数据结构分为线性表,树,图,hash)但我觉得集合才是取代图的概念,而不是图,图只是节点间的逻辑关系居先,一般谈到ordered就是前驱后继的由来,而节点间的连通关系根本不是ordered.
为什么树的ordered会成为划分树所属的逻辑派别的依据呢?比如二叉排序树它并非二叉有序树,为什么要进行这种区别呢(你可以在下一节找到答案)
在讲解算法与数据结构的教科书中(特别是C++用来表达数据结构与算法),有一种语言抽象机制屡屡被谈到,这就是ADT.
C时代是POD类型与过程式语法,这个时候数据与代码是紧密相连的,再后来出现了模块化但是面向过程编程,这个时候,利用临时自动变量可以降低子程序间的藉合度,而当当语言支持UDT和ADT系统时,出现面向对象后,我们完全可以先定义一个预对象(什么是预呢,就是说这个类被写出来的那一瞬那,它并不像面向过程子程序一样存在某个一个rotuine执行路径中,而是作为预定义的一个程序组件,除非你定义并引用了一个该类的实例,这个以前定义的类中的代码才会进入某个执行路径),这样,模块化就做到充分了,即类,一个类是对象是预定对象变量与对象实例(注意这二个概念,变量可以是一个指针,是声明性的并不分配内存
类是真正的数据,,比类的成员数据(POD,C的plain old data,数据结构学中,我们从来不是大量需要数组,链表这样的底层ADT,而是需要更抽象的比如堆栈队列,红黑树,hash map这样的更为抽象的东西)更能代表数据的意义,我们的程序就是一个一个的数据,类是用用户的观点来表达现实生活中出现的各种各样的数据的最好方式,因此会有面向对象数据库的出现(数据库技术中,关系模式的下表达的数据给人的引象似乎是它是为专门的数值数据而建立的)
ADT就是指封装了事物属性跟作用的指代物本身,它的属性和作用是ADT之所以为ADT的意义所在.即数据类型不再是基本类型,而是结合了数据跟代码的抽象了的模型(是一种抽象数据类型)
类实际上是一种数据抽象(也是一种代码抽象),而不是一种数据结构,,因为它将代码抽象为一个一个的“数据模式”,即将C++这样的通用语言8增加DSL词汇,让它成为DSL,使之可以表达Class Cat,Class Pig,阿猪阿猫阿狗诸如这样的领域词汇,所以类是一种数据抽象模式(解决编译器向计算机提供编码实现的数据模式问题,把词汇抽象为语言的一级first class数据,即UDT9,ADT这里面D的意义),也是代码模式(解决对于语言的代码抽象问题,计算机操作什么样的代码来影响这些逻辑和数据),而数据结构学是一种实现模式,而不是代码模式.
数据结构学与代码结构学的区别,是解决问题的问题和解决语言映射问题10的区别,两者在不同抽象层次,这就是为什么数据结构可以用任何语言可以用基本流程实现也可以C++的类来实现,因为数据结构学跟它如何用一种代码结构来抽象是没有直接关联的,前者是如何解决问题的抽象11.