物以类聚。
----------------------------------------------------------------------------------------------------
0002:
人们把同类的事物放在一起考虑,就组成了所谓的集合。
----------------------------------------------------------------------------------------------------
0003:
我们把具有共同性质的事物的全体称为集合。组成集合的每个事物称为这个集合的元素。
----------------------------------------------------------------------------------------------------
0004:
数据是指对客观事物的符号表示,在计算机科学中,我们把所有能输入到计算机中,并能被计算机处理的符号
总称为数据。换言之,它就是计算机程序加工的原料。
----------------------------------------------------------------------------------------------------
0005:
数据结构就是相互之间存在一种或多种特定关系的数据元素的集合,数据元素之间存在的相互关系就叫结构。
根据数据元素之间关系的不同性质,通常有下列四类基本结构:
●集合:结构中的数据元素之间除了同属于一个集合的关系外,别无其它关系。
●线性结构:结构中的数据元素之间存在一个对一个的关系。
●树形结构:结构中的数据元素之间存在一个对多个的关系。
●图状结构:结构中的数据元素之间存在多个多对多个的关系。
见图0005
----------------------------------------------------------------------------------------------------
0006:
不同的数据类型规定了在程序执行期间不同的变量的取值范围和允许的操作,所以,数据类型是一个值的集合
和定义在这个值集上的一组操作的总称。例如,整数类型,它定义了其值集为区间[-max~max],并规定了在这个值
集合上允许的一组操作:+、-、*、/等。
----------------------------------------------------------------------------------------------------
0007:
对算法的分析和评价通常较复杂,一般需考虑正确性、简单性、最优性、运算量及占用存储量等诸多因素。
----------------------------------------------------------------------------------------------------
0008:
线性数据结构的基本接口:
●初始化:建立一个空的线性表
●求长度:计算线性表的长度,也就是线性表中元素的个数
●存取元素:获取第i个元素
●定位:元素x所对应的位置
●插入新元素:增加一个新的元素
●删除元素:删除一个已经在线性表中的元素
●判空:判断线性表是不是为空
除了上面的基本操作外,对线性表还可以进行一些更复杂的运算。如:将两个或两个以上的线性表合并成一个
线性表;把一个线性表分解成两个或两个以上的线性表;重新复制一个线性表;对线性表中的数据元素按某个数据
项递增(或递减)的顺序进行重新排列等。这些操作均可利用上述的基本运算来实现。
----------------------------------------------------------------------------------------------------
0009:
数据结构接口的确定比实现还重要。
----------------------------------------------------------------------------------------------------
0010:
在计算机内存中,可以用不同的方式来存储一个线性表:顺序存储和链式存储。
----------------------------------------------------------------------------------------------------
0011:
在线性表的顺序存储结构中,如果在第一个元素之前插入新元素则要将线性表中所有的元素全部向后移动一个
位置;只有在表尾插入一个新元素时,才不需要移动数据。因此,插入的效率与插入数据的位置有关系。
----------------------------------------------------------------------------------------------------
0012:
线性表的顺序存储结构,其特点是逻辑关系上相邻的两个元素在物理位置上也相邻。线性表的链式存储结构是
用一组任意的存储单元存储线性表中的数据元素;这组存储单元可以是连续的,也可以是不连续的。因此,为了表
示数据元素a[i]与其后继元素a[i+1]之间的逻辑关系,a[i]除了存储元素本身的信息之外,还需存储指示其直接后
继的信息。这两部分信息组成数据元素a[i]的存储映射,称为结点(node),它包含两个域:一个域用于储存数据
元素的信息称为数据域;另一个域用于存储直接后继元素的存储位置称为指针域(或链域)。
----------------------------------------------------------------------------------------------------
0013:
在线性表的链式存储结构中,数据元素之间的逻辑关系是由线性链表中结点的指针域表示,换句话说,指针是
数据元素之间逻辑关系的映射,而结点在存储器中的位置是可以任意安排的。另外,在线性链表中必须指出第一个
元素的存储地质,所以需要设立一个特殊的指针,称为头指针。而由于最后一个元素没有直接后继,所以它的指针
域的值为空。在线性链表中,当头指针为空时,表示线性表为空表。
----------------------------------------------------------------------------------------------------
0014:
线性链表在执行插入操作前后,只需要改变两个结点的指针域,并未对数据元素作任何移动。不过其缺点是浪
费了存储空间来保存指针。
----------------------------------------------------------------------------------------------------
0015:
在线性链表中,只有一个指向直接后继结点的指针域,所以从一个结点出发只能根据指针往后搜寻其它结点,
而不能直接搜寻结点的直接前驱结点。若要寻找一个结点的直接前驱,则需从头指针开始搜寻。为了克服线性链表
单向性的缺点,提出了双向链表概念。所谓双向链表就是在链表的每个结点中除了设置数据域以外,再有两个指针
域,其一和单链表一样用于指向直接后继结点,另一个则用于指向直接前驱结点。
----------------------------------------------------------------------------------------------------
0016:
在双向链表中,插入操作和删除操作都需要同时修改两个方向上的指针。
----------------------------------------------------------------------------------------------------
0017:
从数据结构角度看,栈和队列也是线性表,数据元素之间存在线性关系,只是栈和队列的基本操作是线性表的
子集,它们是操作受限的线性表。
----------------------------------------------------------------------------------------------------
0018:
栈就是限定仅在表的同一端进行插入数据元素或删除数据元素的线性表。允许插入数据元素和删除数据元素的
一端称为栈顶,而表中固定的一端称为栈底。不含任何元素的栈称为空栈。
----------------------------------------------------------------------------------------------------
0019:
栈又称后进先出线性表(Last in First out,简称LIFO)。
----------------------------------------------------------------------------------------------------
0020:
栈数据结构的接口:
●初始化:创建一个空栈
●判断为空:判断栈中是否有元素
●入栈(push):把元素x压入栈顶
●出栈(pop):弹出栈顶元素
●取栈顶元素不弹出
----------------------------------------------------------------------------------------------------
0021:
栈的存储结构同线性表一样有两种:顺序存储和链表存储。
----------------------------------------------------------------------------------------------------
0022:
在一些大型软件系统中,往往会同时使用多个栈。可以为每一个栈安排一个数组,但这样做并不实际。因为各
个栈的实际使用空间在使用期间是不断变化的,常常会有这样的情况:其中某一个栈溢出时,而另外的栈还有许多
的空间空闲。所以,如果能使多个栈共享空间,则提高了空间的使用效率,从而减少发生栈的溢出。
在实际应用中,需要设立两个栈时,可以使它们共享一维数组空间,两个栈的栈底分别设在数组的两端。入栈
操作时都向中间延伸,仅当两个栈的栈顶指针在中间相遇时才发生溢出。
----------------------------------------------------------------------------------------------------
0023:
队列是限定在表的一端进行插入数据元素,在表的另一端进行删除数据元素的线性表。其中,允许插入的一端
称之为队尾,允许删除的一端称之为队头。当队列中不含任何元素时称为空队列。
----------------------------------------------------------------------------------------------------
0024:
队列又称为先进先出(First in First out,FIFO)线性表。
----------------------------------------------------------------------------------------------------
0025:
队列的数据结构接口:
●初始化:建立一个空队列
●判断队列为空:判断其中是否存在元素
●入队列:把数据元素x插入到队列的队尾
●出队列:若队列不为空,则把队头元素删除,并返回其值
●取队头元素,但不出队
----------------------------------------------------------------------------------------------------
0026:
算术表达式的计算是程序设计语言编译中的一个最基本的问题,它的实现是栈应用的典型例子。广为使用的算
术表达式的计算算法:算符优先算法。
----------------------------------------------------------------------------------------------------
0027:
任何一个算术表达式都是由操作数,运算符和界符组成。
----------------------------------------------------------------------------------------------------
0028:
编译器对算术表达式使用算府优先算法进行翻译计算时,需要设立两个工作栈:一个称为算符栈(OPTR),用
以存储算符;另一个称为操作数栈(OPND),用于存储操作数和中间结果。
----------------------------------------------------------------------------------------------------
0029:
串又称字符串,是由零个或多个字符组成的有限序列。可以把串看作是以字符为数据元素的线性表。串中任意
多个连续的字符组成的子序列称为该串的子串,包含子串的串相应的称为主串。
----------------------------------------------------------------------------------------------------
0030:
当两个串长度相等且对应字符都相等时,称这两个串是相等的。
----------------------------------------------------------------------------------------------------
0031:
串数据结构的接口:
●赋值操作
●判等
●连接
●求长度
●获取子串
●定位字串的位置
●替换
●插入
●删除
----------------------------------------------------------------------------------------------------
0032:
为了便于操作,当用链表存储串值时,除设置指示第一个结点的首指针外还可以附设一个尾指针,指向链表中
最后一个结点,并记录当前串的长度。这样在获取串的长度时根本不需要计算。
----------------------------------------------------------------------------------------------------
0033:
在链式存储结构中,结点大小的选择是很重要的,它直接影响着串的处理效率。存储密度越小(如结点大小为1),
处理越方便,然而存储占用量越大。特别是如果在串的处理过程中需要经常进行内存和外存交换数据的话,则会因
为存储密度小,而使内存和外存交换操作过多,影响处理的总效率。
----------------------------------------------------------------------------------------------------
0034:
无论是采用顺序存储结构还是链式存储结构,它们都存在一些弊病。当用顺序存储结构存储串值时,由于在串
的类型定义中必须预先规定串值允许的最大长度,而在一般情况下,串的长度变化范围较大,所以当多数串的长度
较短时空间的利用率很低;而另外则由于限定了串的最大长度,使串的某些操作如:连接、置换等受到长度的限制。
当用链式存储结构时,虽然链表的结构比较灵活,使串的长度不受限制,但却受到存储密度的制约,如果提高了存
储密度,必然使串的操作复杂化。
在很多实际应用的串处理系统中,对串采用一种动态的存储结构称为堆结构。它就是在系统中开辟一个容量很
大、地址连续的存储空间中分配一个大小和串的长度相同的、地址连续的存储空间用于存储新串的值。这样所有串
的串值都存储在这个可利用空间中。同时为每个串建立一个索引,以记录该串的长度以及其串值在可利用空间中的
起始位置。
----------------------------------------------------------------------------------------------------
0035:
从根本上来说,内存就是一个链式表结构。
----------------------------------------------------------------------------------------------------
0036:
字串的定位操作就是求字串在主串中的位置,通常称为串的模式匹配。其算法的基本思想是:从主串s的第一个
字符起和模式的第一个字符进行比较,如果相等继续逐个比较后续字符;否则从主串的第二个字符起再重新和模式
的第一个字符进行比较。依次类推,直至模式中的每个字符依次和主串中相应字符相等,则称模式匹配成功;否则
模式匹配不成功。
----------------------------------------------------------------------------------------------------
0037:
树是n(n>=0)个结点的有限集。当n=0时,称为空树;当n>0时,一棵树中,有且仅有一个特定的称为根的结点,
而除根以外的其它结点可分为m(m>=0)个互不相交的有限集,其中每个有限集本身又是一棵树,并且称为根的子树。
----------------------------------------------------------------------------------------------------
0038:
在树的定义中又用到了树的概念,所以这是一个递归的定义;这也说明了树的固有特征,在树中每一个结点都
是该树中的某一棵子树的根。
----------------------------------------------------------------------------------------------------
0039:
树的结点包含一个数据元素以及若干指向其子树的分支。结点拥有的子树称为结点的度。树中度不为0的结点称
为分支结点。除根结点之外的分支结点称为内结点。度为0的结点称为叶子。
----------------------------------------------------------------------------------------------------
0040:
森林是m(m>=0)棵互不相交的树的集合。森林的概念与树非常接近,只要把一棵树的根结点去掉,就可以变成
森林。反之,如果把由m棵树组成的森林,加上一个根结点而把这m棵树作为此根的子树,则使森林变成了树。
----------------------------------------------------------------------------------------------------
0041:
树结构的接口:
●初始化:创建一棵空树
●求根
●求父结点
●求子结点
●求相同父结点下的子结点
●插入子树操作
●删除子树操作
●遍历操作:按某个次序依次访问树中各个结点,并使每个结点只被访问一次
----------------------------------------------------------------------------------------------------
0042:
树的存储结构有多种形式:
●父结点表示法
●子结点表示法
●子结点、兄弟结点表示法
----------------------------------------------------------------------------------------------------
0043:
二叉树是一种应用广泛的特殊的树形结构,它的特点是每个结点最多只能有两个子结点。在二叉树中,必须严
格区分左右子结点,其次序不能颠倒,若改变次序则变成另一棵二叉树。
----------------------------------------------------------------------------------------------------
0044:
二叉树的接口:
●初始化操作
●求根结点
●求父结点
●求子结点
●求兄弟结点
●插入子树
●删除子树
●遍历树
----------------------------------------------------------------------------------------------------
0045:
二叉树的性质:
●在一棵二叉树中,第i层的结点数最多为2的(i-1)次方个(i>=1)
●深度为h的二叉树上结点总数最多为2的h方减1个
●具有n的结点的完全二叉树的深度为[log2n]+1
●如果对一棵具有n个结点的完全二叉树的结点进行顺序编号(从上而下,从左到右),则其中任一结点i有:
☆如果i=1,则结点i是二叉树的根;如果i>1,则其父结点的编号为[i/2]
☆若2i>n,则结点i无左兄弟结点;否则其左兄弟结点的编号为2i
☆若2i+1>n,则结点i无右兄弟结点;否则其右兄弟结点的编号为2i
----------------------------------------------------------------------------------------------------
0046:
遍历二叉树是指以一定的次序,访问二叉树中的每个结点,且每个结点只被访问一次。在这里“访问”的含义
很广,可以理解为输出结点中数据的值或对结点中的数据进行处理等。遍历二叉树的过程实际上就是把二叉树中的
结点进行线性排列。由于二叉树中有两种分支,所以遍历的次序不同得到的结果也就不同。设L、D、R分别表示遍历
左子树、访问根结点、遍历右子树,则对一棵二叉树有六种遍历方案:DLR、LDR、LRD、DRL、RDL、RLD。如果在左
右子树的遍历次序上规定先左后右,则只有三种遍历方案:先根遍历(DLR)、中根遍历(LDR)、后根遍历(LRD)。
----------------------------------------------------------------------------------------------------
0047:
一般树转化为二叉树:
●加线
●抹线
●旋转
见图0047
----------------------------------------------------------------------------------------------------
0048:
森林转化为二叉树:森林是树的有限集合
●首相将各棵树分别转换为二叉树
●然后再按森林中树的次序,依次将后一棵二叉树作为前一棵二叉树根结点的右子树。这样,第一棵树的根就
是转化后二叉树的根
见图0048
----------------------------------------------------------------------------------------------------
0049:
从树中一个结点到另一个结点之间分支构成这两个结点之间的路径。路径上分支的数目称为路径长度。树的路
径长度是从根结点到所有结点的路径长度之和。在具有n个结点的二叉树中,完全二叉树具有最小的路径长度,但是
具有最小路径长度的不一定是完全二叉树。
见图0049
----------------------------------------------------------------------------------------------------
0050:
在静态查找表中,如果各记录的次序是按其关键字的大小顺序排列的,则称为有序表。对顺序存储的有序表可
采用折半查找。
折半查找的基本思想是:先取表的中间位置记录的关键字同给定值进行比较,如果给定值与该记录的关键字值
相等,则查找成功;否则,分二种情况,分别在表的前半部分或后半部分进行查找(当给定值小于中间记录的关键
字值时,在前半部分进行查找;否则,在后半部分进行查找)。如果反复,直到找到或者查找区间小于零为止。
----------------------------------------------------------------------------------------------------
0051:
折半查找过程可用二叉树来描述。二叉树中每个结点对应有序顺序表中的一个记录,结点中的值为该记录在表
中的位置。找到一个记录的过程恰好是走了一条从根结点到该记录对应结点的路径,和给定值进行比较的次数恰好
为该结点在此二叉树上的层次数。
----------------------------------------------------------------------------------------------------
0052:
无论是顺序查找、折半查找还是二叉排序树查找,都要通过一系列的关键字比较才能确定被查记录在表中的位
置;所以,这类方法统称为对关键字进行比较的查找方法。而哈希法却是利用关键字进行转换,计算出记录存放地
址的查找方法。
如果一个关键字对应一个地址,这就是最直观,最简单的哈希法。但这种方法往往行不通。如果一个关键字对
应一个地址,那其存储空间就难以满足。因此,这样的一一对应关系虽然简单、方便,但是实用价值不大。
由于希望尽量减少存储空间,所以会造成两个不同的关键字被转换到同一个存储地址上去,此时叫做发生冲突。
如何利用关键字直接转换成存储地址呢?这需要设计一个函数,其自变量是关键字,这个函数称为Hash函数,用H
表示。该函数把变化范围很广并可识别的关键字通过各种剪裁手段和简单的数学运算,转换成存储地址,因此得名
为杂凑。由于可以把Hash函数看成按照某种特定的方法将记录按关键字散列到内存储器的指定空间内,所以这种方
法也称为散列地址法。另外,也可以称为关键字转换法。采用哈希法在连续的内存空间中建立起来的符号表,就称
为哈希表。
实际上,冲突是不可避免的,所以只要求发生冲突的可能性尽可能小。
----------------------------------------------------------------------------------------------------
0053:
衡量Hash函数好坏的主要标准是尽可能减少冲突。要减少冲突,就要设法使哈希函数尽可能均匀的把关键字映
射到符号表存区的各个存储地址上,这样可以提高查找效率。
----------------------------------------------------------------------------------------------------
0054:
常用的几种哈希函数算法:
●平方取中法:先计算出关键字内部代码的平方值,再取它的中间几位作为内存地址。
●除留余数法:用模运算。设给出的关键字为k,存储区单元数为m,则用一个小于m的质数p去除k,得到余数r。
如果r落在存储区地址范围内,则r就取为哈希函数值;否则,再用一个线性函数求出哈希函数值。p的选择很重要,
理论和实验证明,p取小于存储区域的最大的素数比较好。能获得比较好的均匀性。
●数字分析法
----------------------------------------------------------------------------------------------------
0055:
哈希法不可避免冲突。解决冲突的方法基本上有两大类:
一类称为开发地址法,当发生冲突时,用某种方法形成一个探测的序列,沿着这个序列一个个单元地查询,直
到找到这个关键字的记录或找到一个开放的地址(即没有存储的空单元)。此时,如果是插入操作,则遇到空单元
就可以进行插入;如果是查找操作,则遇到空单元就是查找失败。
另一类称为链地址法,当发生冲突时,就拉出一条链,建立一个连接方式的子表,使具有相同哈希函数值的关
键字及其记录连接在同一个子表中。
----------------------------------------------------------------------------------------------------
0056:
链地址法是经常使用的处理冲突的方法,此方法很有效,它将所有哈希地址相同的记录存储在一个线性链表中。
----------------------------------------------------------------------------------------------------
0057:
在哈希表上进行查找的过程同哈希表的构造过程基本一致。设给定值为k,根据造表时设定的哈希函数求出哈希
地址,若表中此位置上没有记录,则查找不成功;否则,比较关键字,若给定值相等,则查找成功;否则说明发生
冲突,根据造表时设定的处理冲突的方法找下一地址,直至哈希表中某个位置为空,或者此位置记录的关键字等于
给定值时为止。
----------------------------------------------------------------------------------------------------
0058:
哈希法是利用关键字进行转换计算后直接求出存储地址的。所以当由哈希函数能得到均匀的地址分布时,不需
要进行比较就可以直接找到所查的记录。但实际上,冲突不可避免,因此查找时还需要进行探测、比较,查找的效
率显然取决于发生冲突的次数。
----------------------------------------------------------------------------------------------------
0059:
有时候排序是为了提高查找的速度,例如,折半查找的前提就是记录按关键字必须有序,有时候排序是系统管
理上的需要。
----------------------------------------------------------------------------------------------------
0060:
如果待排序的记录中,存在关键字相等的记录,当经过排序后,这些关键字相等的记录之间,相对次序保持不
变,则称这种排序方法是稳定的,否则称为不稳定的。
----------------------------------------------------------------------------------------------------
0061:
插入排序:插入排序的基本思想是把一个个记录按其关键字的大小插入到已经排好次序的记录序列中,使得插
入后的记录序列仍然是有序的。
----------------------------------------------------------------------------------------------------
0062:
希尔排序又称“缩小增量排序”,它属于插入排序的一种,是对插入排序方法的改进。从对直接插入排序的分
析得知,其算法的平均时间复杂度为O(n2次方)。但是,当待排序记录序列按关键字为“正序”时,其时间复杂度可
降低到O(n)。由此可设想,当待排序记录按关键字“基本有序”时,直接插入排序的效率就可大大提高;从另一方
面来看,直接插入排序算法简单,并且在n值很小时效率也比较高。希尔排序正是从这两点分析出发对直接插入排序
进行改进而得到的一种插入排序方法。其算法思想是:选定一个记录的间隔数d,把全部记录按此间隔数从第一个记
录起进行分组,所有相隔为d的记录作为一组,在各组内进行排序;然后减小间隔数,重新分组,再进行组内排序。
如此重复,直到间隔数为1。各组的组内排序可以用直接插入排序,也可以用其它的排序方法。
希尔排序的速度一般要比直接插入排序快,但具体分析比较复杂,因为它的时间是所取“增量”的函数。希尔
排序的评剧比较次数和平均移动次数越为n1.3次方。希尔排序是不稳定的。
----------------------------------------------------------------------------------------------------
0063:
冒泡排序方法是记录按纵向排序,然后自下而上的比较相邻记录的关键字K[j]和K[j-1],若K[j-1] > K[j]称为
逆序,则两者交换位置。再将K[j-1]与K[j-2]进行比较,如有逆序则交换。直至全部关键字均比较一遍。这样一次
加工就使关键字最小的记录上升到第一个位置。然后进行第二次冒泡排序,依次类推,直到全部排序完成。
在算法中应记住每一次排序时,是否发生过交换,若没有发生过交换则说明已排好序,整个排序结束。
时间复杂度为O(n2次方)。冒泡排序是一种稳定的排序方法。
----------------------------------------------------------------------------------------------------
0064:
快速排序是由霍尔提出的。这是目前内部排序中速度较快的方法,故称为快速排序。其实质是分区交换排序。
在这种排序方法中,用一个向量存储n个记录,先取向量中第一个记录作为控制记录,设法把该记录放到向量的合适
位置上,使得在这个记录右面的所有记录的关键字均大于等于它的关键字,而在它左面的那些记录的关键字都小于
它的关键字,这样就把序列分成了两个子序列,此过程称为一次快速排序。然后再分别对这两个子序列进行下一次
快速排序,依次类推,直到排序完成。
----------------------------------------------------------------------------------------------------
0065:
直接选择排序的方法是首先在所有的记录中选出关键字最小的记录,将它与第一个记录交换位置存储;然后再
在余下的记录中选出关键字最小的记录,将它同第二个记录交换存储位置。依次类推,直至选出所有的记录,使整
个序列成为有序序列。
直接选择排序的主要操作是记录关键字的比较,所以其总的时间复杂度为O(n2次方)。直接选择排序是一种不稳
定的排序方法。
----------------------------------------------------------------------------------------------------
0066:
归并排序是另一种类型的排序方法。归并的意思是把两个或两个以上的有序序列合并起来,形成一个新的有序
序列。开始时,可把一个有n个记录的无序序列看成为n个只一个记录的有序子序列,然后进行两两归并,最后形成
一个有n个记录的有序序列。
----------------------------------------------------------------------------------------------------