程序设计=数据结构+算法
(1)数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。
(2)逻辑结构(面向问题)和物理结构(面向计算机)。
(3)逻辑结构:是指数据对象中数据元素之间的相互关系。(集合结构,线性结构,树形结构,图形结构)
(4)物理结构(存储结构):是指数据的逻辑结构在计算机中的存储形式。(顺序存储和链式存储)
(5)顺序存储结构:连续的存储单元,逻辑与物理一致
(6)链式存储:任意存储单元,可连续,可不连续。
(7)抽象数据类型(Abstract Data Type, ADT):是指一个数学模型及定义在该模型上的一组操作。
(8)算法:解决确定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
(9)算法的基本特性:输入,输出,有穷性,确定性和可行性。
(10)好的算法:时间效率高和存储量低。
(11)算法效率关注主项(最高阶项)的阶数。
(12)算法的时间复杂度。大O记法,O(1)常数阶,O(n)线性阶,O(n2)平方阶,O(logn)对数阶。
(13)O(1)
(14)算法的空间复杂度: S(n)=O(f(n)), n表示问题规模,f(n)为语句关于n所占存储空间的函数。
(1)零个或多个数据元素的有限序列。
(2)前驱元素,后继元素。第一个元素无直接前驱,最后一个元素无直接后继,中间元素仅有单一直接前驱和直接后继。
(3)线性表的顺序存储结构:用一段地址连续的存储单元依次存储线性表的数据元素。它的存取时间性能为O(1),具有这一特点的存储结构称为随机存取结构。插入或删除为O(n)。
(4)线性表的链式存储。结点(node):数据域,指针域。
(5)单链表:每个结点中只包含一个指针域。链表中第一个结点的存储位置叫做头指针。线性链表的最后一个结点指针为“空”(NULL或“^”)
(6)在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可不存储任何信息。也可以存储线性表长度等公共数据。
(7)若线性表为空,则头结点的指针域为“空”。
单链表结构 | 顺序存储结构 | |
---|---|---|
存储分配方式 | 任意存储单元存放线性表 | 连续存储空间,依次存储 |
时间性能 | 查找O(n);插入和删除O(1) | 查找O(1) ;插入和删除O(n) |
空间性能 | 不需要分配存储空间,元素个数不受限制 | 预分配空间大了浪费,小了上溢 |
(8)静态链表:用数组描述的链表。(游标实现法)
(9)在动态链表中,结点的申请和释放分别借用malloc()和free()两个函数实现。在静态链表中则不行。
(10)静态链表的优缺点
优点 | 缺点 |
---|---|
在插入与删除时,只改游标cur;不做大量移动 | 未解决连续存储分配带来的表长难以确定的问题;失去了顺序存储结构随机存取的特性 |
(11)循环链表:终端结点的指针由空指向头结点,单循环链表。尾指针。
(12)双向链表(double linked list):在单链表中,再设置一个指向前驱结点的指针域。
(1)栈(stack):后进先出(Last In f=First Out),LIFO结构,仅在表尾进行插入和删除操作的线性表;栈顶(top),栈底(bottom),空栈;
(2)表尾即栈顶(top),进栈(压栈或入栈)(push);出栈(弹栈)(pop);
(3)小例子:
1,2,3依次进栈,不存在312这样的出栈顺序,思考?
(4)栈的顺序存储结构,顺序栈。
(5)两栈共享空间(两居室);++top1 --top2;一个栈在增长,一个栈在缩短。
(6)栈的链式存储结构,(链栈),基本不存在栈满的情况;将头结点作为栈顶指针top;
(7)栈的大小不可预料,多用链栈;大小在可控范围内,则使用顺序栈,好一些。
(8)栈的应用1:递归(前行和回退),,,,,,,斐波拉契数列(fibonacci)两种实现方法:迭代和递归。
迭代是循环结构,递归是选择结构。递归更简洁,但大量递归调用会建立函数的副本,会耗费大量的时间和内存。迭代不需要反复调用函数和占用额外的内存。
(9)栈的应用2:四则运算表达式求值,,,,
一种不需要口号的后缀表达法,(逆波兰(Reverse Polish Notion,RPN)).931-3*+102/+ 遇到数字就进栈,遇到符号就将处于栈顶的两个数字出栈,计算,再将计算结果进栈。从而获得最后的计算结果。
我们平时所用标准四则运算表达式,叫中缀表达式。9+(3-1)*3+10/2
(1)队列:先进先出(FIFO),仅在一端插入(队尾),另一端删除(队头)的线性表;
(2)队列顺序存储的不足,,也有front,rear。后排满,前排空,的“假溢出”。
(3)循环队列(顺序存储):队头(front)指向队头元素。队尾(rear)指向队尾元素的下一个位置。头尾相接的顺序存储结构。
(4)判断循环队列满的方法
(5)考虑第2种,保留一个空间。
(6)链队列:队列的链式存储
(7)确认队列长度时,用循环队列;无法预估队列长度,用链队列。
(1)串:由零个或多个字符组成的有限序列,又叫字符串。
(2)空串(null string):
(3)在计算机中存在一个自由存储区,焦作“堆”。这个堆可由C语言中的malloc()和free()来管理。
(4)串的顺序存储结构与链式存储结构。其中,链式存储结构中,一个结点可以存放一个字符,也可以存放多个字符。若一个结点未占满,可用“#”或其他非串值字符补全。
(5)串的模式匹配:子串定位操作。
(6)朴素的模式匹配算法:单个字符逐一匹配,单个字符往后移动,挨个遍历。
(7)KMP模式匹配算法:仅当模式与主串之间存在许多“部分匹配”的情况下才体现出它的优势,否则两者差异并不明显。
(8)改进KMP算法,参考算法导论,第2版32章字符串匹配。
(1)树是n(n>=0)个节点的有限集。n=0为空树。
(2)在任意非空树中:
(3)度(Degree):结点拥有的子树数;
(4)叶结点或终端结点(Leaf):度为0的结点;
(5)非终结点或分支结点:度不为0;
(6)内部结点:根结点,分支结点。树的度是树内部结点的度的最大值。
(7)结点的子树称为该结点的孩子(child),相应地,该结点称为孩子的双亲(Parent)。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根到该结点****所经分支上的所有结点。以某结点为根的子树中的任一结点都称为该结点的子孙。
(8)结点的层次(level):根为第一层,根的孩子为第二层。双亲在同一层的,互称为堂兄弟。
(9)树的深度(Depth)或高度:树中结点的最大层次。
(10)有序树:树中结点的各子树看成从左至右是有次序的,不能互换的。否则,称为,无序树。
(11)森林(Forest):m(m>=0)棵互不相交的树的集合。
线性结构 | 树结构 |
---|---|
第一个数据元素:无前驱 | 根结点:无双亲,唯一 |
最后一个数据元素:无后继 | 叶结点:无孩子,可以多个 |
中间元素:一个前驱一个后继 | 中间结点:一个双亲多个孩子 |
(1)双亲表示法:每个结点中,附设一个指示器指示其双亲结点在数组中的位置。
data | parent |
---|
data是数据域,存储结点的数据信息;parent是指针域,存储该结点的双亲在数组中的下标。
data | parent | firstchild |
---|
增加一个长子域,存储长子的下标,对于有0或1个孩子的结点来说,这样的结构解决了要找结点孩子的问题,甚至是有2个孩子,知道长子是谁,另一个当然就是次子。
data | parent | rightsib |
---|
增加右兄弟域,体现兄弟关系。存储右兄弟的下标。
小结:上述结构可以组合,存储结构的设计是一个非常灵活的过程,一个存储结构设计得是否合理,取决于该存储结构的运算是否适合、是否方便,时间复杂度好不好等。
(2)孩子表示法:
多重链表表示法:每个结点有多个指针域,其中每个指针指向一棵子树的根结点。
data | child1 | child2 | child3 | … | childd |
---|
data是数据域,child1到childd是指针域,用来指向该结点的孩子结点。
方案一:指针域的个数等于树的度。
方案二:指针域的个数等于该结点。
data | degree | child1 | child2 | child3 | … | childd |
---|
**具体解决办法:**把每个结点的孩子结点排列起来,用单链表做存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后,n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。
但,在这个结构中,要知道某个结点的双亲,需要遍历所有结点。此处可以把双亲表示法与孩子表示法结合一下。双亲孩子表示法。
(3)孩子兄弟表示法
data | firstchild | rightsib |
---|
任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。data数据域,firstchild指向该结点的第一个孩子结点,rightsib指向该结点的右兄弟结点。
这种方法将复杂的树,表示成了一棵二叉树。
(1)定义:二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
(2)二叉树的特点:
(1)斜树(左斜树,右斜树)
(2)满二叉树:
(1)第i层,最多有2i-1个结点
(2)深度为K,至多有2k-1个结点
(3)终端结点数为n0度为2的结点数为n2,则n0=n2+1。利用分支数推导
(4)具有n个结点的完全二叉树的深度为[log2n]+1。([x]表示不大于x的最大整数)
(5)如果对一棵有n个结点的完全二叉树,对任意结点i
(1)二叉树的顺序存储结构
完全二叉树按层序编号存储在数组中;对于一般二叉树,可以对没有结点的地方留空“^”
(2)二叉链表
lchild | data | rchild |
---|
(1)二叉树的遍历,所有结点,访问一次且仅被访问一次。
(2)二叉树遍历方法:
注意:
但已知前序和后续,则不行。如前序ABC,后续CBA,则有4种情况
(1)普通二叉树;
(2)扩展二叉树:每个结点的空指针引出一个虚结点“#”。
(1)n个结点的二叉树,(二叉链表存储),有n-1条分支,存在2n-(n-1)=n+1个空指针域。
(2)指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。将二叉树转变成一个双向链表。
(3)对二叉树以某种次序遍历使其变为线索二叉树的过程承做是线索化。
lchild | ltag | data | rtag | rchild |
---|
ltag=0,该结点指向左孩子,=1指向前驱
rtag=0,该结点指向右孩子,=1指向后继
(4)线索化的过程就是遍历的过程中修改空指针的过程。
(1)树转二叉树:
(2)森林转化为二叉树
(3)二叉树转换成树(逆过程)
(4)二叉树转换成森林
判断一棵二叉树转换成一棵树,还是森林,看这个二叉树的根结点有没有右孩子即可。有则可以转换成森林,没有则转换成树
(5)树的遍历
(6)森林的遍历
(1)最基本的压缩编码方法
(2)路径长度:一个结点到另一个结点之间路径上的分支数。
(3)树的路径长度:根结点到每一结点的路径长度之和。
(4)结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积
(5)树的带权路径长度:根结点到每一结点的带权路径长度之和
(6)带权路径长度WPL最小的二叉树称作赫夫曼树。(最优二叉树)
(7)赫夫曼算法描述:
(8)前缀编码:任一字符的编码都不是另一个字符的编码的前缀。(设计长短不等的编码)
(9)赫夫曼编码:
(1)图的表示:G(V,E)。V为图G中顶点(Vertex或Node)的集合,要求:有穷非空;E是图G中边的集合,可以为空。
(2)无向边(Edge):无序偶对(vi,vj)表示;无向图
(3)有向边:也称为弧(Arc),有序偶
(4)简单图:不存在顶点到自身的边,不存在同一条边重复出现。
(5)无向完全图:任意两个顶点之间都存在边的无向图。共有n(n-1)/2条边(因计算有重复)
(6)有向完全图:同上,共有n(n-1)条边。
(7)稀疏图:有很少条边或弧。反之,为稠密图
(8)边或弧相关的数叫做权(Weight),带权图称之为网。
(9)子图(Subgraph),与子集概念类似。
(10)(v,v’)属于E,则称顶点v和v’互为邻接点(Adjacent),(v,v’)与顶点v和v’相关联。顶点v的度是和v相关联的边的数目。TD(v)。
(11)有向图中
(12)路径(path):一个顶点序列。路径的长度是路径上的边或弧的数目。
(13)回路或环(Cycle):第一个顶点到最后一个顶点相同的路径。
(14)简单路径:序列中顶点不重复出现的路径
(15)简单回环或简单环:除第一和最后顶点外,其余点不重复的回路。
(16)连通图:任意两个顶点连通的无向图,任意两顶点之间有路径,称之为连通。
(17)无向图中的极大连通子图:连通分量。
(18)在有向图中,任意两点间存在路径,称为强连通图。有向图中的极大强连通子图称为有向图的强连通分量。
(19)连通图的生成树:一个极小的连通子图。n个顶点,n-1条边
(20)有向图中,恰有一个顶点入度为0,其余顶点的入度均为1,则是一棵有向树。有向图生成的森林:由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。
(1)一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储边或弧的信息。无向图对称,有向图不对称。
(2)网:没有边的权值设定为极大值。
(1)将数组与链表相结合的存储方式
(2)处理方式:
注:在有向图中,有逆邻接表,即以顶点为入度关系的边表;可以在边表汇总增加一个权值数据域。
(1)邻接表:关心出度问题;逆邻接表:关心入度问题。两者结合成十字链表。容易获得顶点的出度与入度。
顶点表的结构如下:
data | firstin | firstout |
---|
firstin表示入边头指针,firstout表示出边头指针。
tailvex | headvex | headlink | taillink |
---|
tailvex 指弧起点在顶点表的下标,headvex指弧终点在顶点表的下标;headlink指入边表指针域,指向终点相同的下一条边。taillink指边表指针域,指向起点相同的下一条边。如果是网,可增加Weight。
(1)在无向图中,删除边操作。
重新定义边表结点:
ivex | ilink | jvex | jlink |
---|
ivex,jvex表示与某条边所依附的两个顶点在顶点表中的下标。ilink指向依附顶点ivex的下一条边;jlink指向依附顶点jvex的下一条边。
(1)由两个一维数组组成。一个存储顶点信息,一个存储边的信息。每个边数组由一条边的起点下标(begin),终点下标(end)和权(weight)
适合于对边进行处理操作。
(1)访遍所有顶点,仅访问一次
(2)深度优先遍历(Depth_First_Search),DFS.
(3)广度优先遍历(Breadth_First_Search),BFS
小结:深度优先更适合目标比较明确,以找到目标为主要目的的情况;
而广度优先更适合在不断扩大遍历范围时找到相对最优解的情况。
(1)一个连通图的生成树是一个极小的连通子图。包括图中所有顶点,只有足以构成一棵树的(n-1)条边
(2)最小生成树:构造连通网的最小代价生成树。
(3)经典方法:普里姆算法(Prim)(O(n2))与克鲁斯卡尔(Kruskal)(O(eloge))算法
(1)最短路径:对于非网图即两顶点之间边数最少;对于网图即两点之间边上权值和最小。源点,终点
(2)迪杰斯特拉(Dijkstra)算法(O(n2)):基于已经求出的最短路径的基础之上,求得更远顶点的最短路径。每一步最优。要求任意顶点到其余所有顶点的最短距离时(O(n3)).
(3)佛洛依德(Floyd)算法(O(n3)):求所有顶点至所有顶点的最短路径的问题。
(1)无环的图应用;即图中没有回路。
(2)AOV网(Activity On Vertex Network):表示工程的有向图,顶点表示活动,弧表示活动之间的优先关系。
(3)G=(V,E)为n个顶点的有向图,V中的顶点序列,若vi到vj有一条路径,则在顶点序列中顶点vi必须在顶点vj之前。我们称这样的顶点序列为一个拓扑序列。
(4)拓扑排序:对一个有向图构造拓扑序列的过程。
输出结果有两个:
适用于各种各样的工程或项目的流程图中。
(5)拓扑排序算法思路:
(1)AOE网(Activity On Edge Network):在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的时间,这种有向图的边表示活动的网。
(2)始点或源点(没有入边);终点或汇点(没有出边)。
(3)路径长度:路径上各个活动所持续的时间之和;
(4)关键路径:从源点到汇点具有最大长度的路径;在关键路径上的活动,叫关键活动。
(5)该算法对于工程的前期工期估算和中期的计划调整都有很大的帮助。对于有几条关键路径的有向无环图,需要同时在几条路径上提高速度才行。
(1)查找表(Search Table):同一类型的数据元素(或记录)构成的集合。
(2)关键字(Key):数据元素中某个数据项的值,又称为键值,用它可以标记一个数据元素。也可以标识一个记录的某个数据项(字段),我们称之为关键码。
(3)主关键字(Primary key):可以唯一地标识一个记录。主关键字所在的数据项称为主关键码。
(4)次关键字(Secondary key):可以识别多个数据元素(或记录)的关键字。次关键字所对应的数据项称为次关键码
(5)查找:即给定某个值,在查找表中确定一个其关键字等于给定值得数据元素(或记录)。
(6)静态查找表(Static Search Table):只作查找操作的查找表
(7)动态查找表(Dynamic Search Table)
(1)顺序查找(Sequential Search)又叫线性查找。从表中第一个(或最后一个)记录开始,逐一比较,查找。
(2)优化:设置哨兵,从尾部比较
(1)折半查找(Binary Searc
h)又称二分查找。(O(logn))
(2)插值查找(interpolation search)
(3)斐波拉契查找(Fibonacci Search)
小结:折半查找时加法与除法运算,插值查找进行复杂的四则运算,斐波拉契查找只是简单的加减法运算。
(1)数据按先后顺序存储,海量数据。
(2)索引:是为了加快查找速度而设计的一种数据结构。即把一个关键字与对应的记录相关联的过程。一个索引由若干个索引项构成,每个索引项至少包含关键字和其相对应的记录在存储器中位置等信息。
(3)索引按结构分类:线性索引,树形索引和多级索引。
(4)线性索引:将索引项集合组织为线性结构,也称为索引表。
(5)三种线性索引:稠密索引,分块索引和倒序索引。
(6)稠密索引:
(7)分块索引:
被广泛应用于数据库表查找等技术。
(8)倒排索引
记录号表存储具有相同次关键字的所有记录的记录号(可以是指向记录的指针或是该记录的主关键字)
小注:做好这方面的研究,可否进google或百度做搜索引擎的软件工程师。
(1)又称二叉查找树(中序遍历即可得到从小到大的排列)
(3)二叉排序树的查找性能取决于二叉排序树的形状,但其形状是不确定的,应尽可能的使其深度与完全二叉树相同,均为[log2n]+1,那么查找时间复杂就是O(logn),近似于折半查找,若时左或右斜树,则为O(n),和顺序查找没有区别。
(1)一种二叉排序树,其中每个结点的左右子树的高度差至多等于1。
(2)一种高度平衡的二叉树
(3)将二叉树上每一个结点的左子树深度减去右子树深度的值称为平衡因子BF(Balance Factor)。即BF=-1,0,1则是平衡二叉树
(4)最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树。
(5)平衡二叉树构建的基本思想:在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡树。在保证二叉排序树特性前提下,调整最小不平衡树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
(6)查找时间复杂度:O(logn),插入和删除也一样。
二叉排序树还有其他平衡方法:红黑树(Red Black Tree)
(1)每个结点的孩子数可以多于两个,且每个结点处可以存储多个元素。由于是查找树,所有元素之间存在某种特定的排序关系。
(2)每个结点存储多少个元素,以及它的孩子数是非常关键的部分。4种特殊形式:2-3树,2-3-4树,B树,B+树
(3)2-3树
(4)2-3-4树
(5)B树(B-tree)
(6)B+树
(1)散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。.
(2)f称为散列函数,又称为哈希(Hash)函数。
(3)采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。
(4)散列技术既是一种存储方法,也是一种查找方法。
(5)散列技术最适合的求解问题是查找与给定值相等的记录。
(6)不适用的地方:
(7)冲突:两关键字key1与key2不相等,但f(key1)与f(key2)却相等。此时,key1与key2被称为散列函数的同义词。
(1)要求:计算简单、散列地址分布均匀
(2)常用方法1:直接定址法
(3) 常用方法2:数字分析法
(4)常用方法3:平方取中法
(5)常用方法4:折叠法
(6)常用方法5:除留余数法
(7)常用方法6:随机数法
小结:综合考虑的因素构造散列表
(1)开放定址法
(2)再散列函数法
(3)链地址法
(4)公共溢出区法
(1)散列表查找性能分析
(1)