数据结构与算法

程序 = 数据结构 + 算法

1 数据结构

  • 数据不仅仅包括数值类型,还包括字符及声音、图像、视频等非数值类型。
  • 数据项是数据不可分割的最小单位,数据元素由若干个数据项组成,真正讨论问题时一般以数据元素为单位。
  • 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合,数据元素之间的关系就是结构,4类基本结构:集合,线性结构,树状结构,图状/网状结构。
1.1数据结构分为逻辑结构和物理结构
逻辑结构:是指数据对象中数据元素之间的相互关系。有四种逻辑结构:
  • 集合结构:集合结构中的数据元素除了同属于一个集合内,没有其他关系。
    在这里插入图片描述
  • 线性结构:线性结构中的数据元素之间是一对一的关系。
    在这里插入图片描述
  • 树形结构:树形结构中的数据元素之间存在一种一对多的层次关系。
    在这里插入图片描述
  • 图形结构:图形结构的数据元素是多对多的关系
    在这里插入图片描述
物理结构:是指数据的逻辑结构在计算机中的存储形式。有两种存储结构:
  • 顺序存储结构:是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。
    在这里插入图片描述
  • 链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。数据元素的存储关系并不能反映其逻辑关系,因此需要用一个指针存放数据元素的地址,这样通过地址就可以找到相关联数据元素的位置。
    在这里插入图片描述
1.2数据类型
  • 数据类型是指一组性质相同的值的集合及定义在此集合上的一些操作的总称,用来说明变量或表达式的取值范围和所能进行的操作。

2 算法

  • 算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
2.1算法的基本特性
  • 输入输出特性:零个或者多个输入,至少一个或多个输出。
  • 有穷性:指算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。
  • 确定性:算法的每一步骤都具有确定的含义,不会出现二义性。
  • 可行性:算法的每-步都必须是可行的,也就是说,每- -步 都能够通过执行有限次数完成。
    算法设计的要求:正确性、可读性、健壮性(输入不合法时也能处理)、时间效率高和存储量低。
2.2算法效率的度量方法
  • 时间复杂度(运算时间):执行次数函数的阶级。通常,提到的运行时间都是最坏情况的运行时间。
    在这里插入图片描述
  • 空间复杂度(存储空间)。

3 线性表

线性表(List):零个或多个数据元素的有限序列。线性表元素的个数n定义为线性表的长度,当n=0时,称为空表。在较复杂的线性表中,一个数据元素可以由若干个数据项组成。
在这里插入图片描述

3.1线性表的顺序存储

可以用一维数组来实现线性表的顺序存储结构。在任意时刻,线性表的长度应该小于等于数组的长度。
采用顺序存储结构,优点:无须为表示表中元素之间的逻辑关系而增加额外的存储空间;可以快速地存取表中任位置的元素。缺点:插入和删除操作需要移动大量元素;当线性表长度变化较大时,难以确定存储空间的容量

3.2线性表的链式存储

对第i个数据元素来说,除了存储其本身的信息之外,还需存储第i+1个元素的存储位置。
在这里插入图片描述
在这里插入图片描述
这样的线性表称为单链表,是一种动态结构,可以动态地分配存储的,即在需要时才开辟结点的存储空间,实现动态链接。
若线性表中元素个数变化不大,需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。

3.3静态链表
  • 在某些高级语言中,没有指针类型,所以想使用链表,得靠其它手段,比如静态链表。静态链表:线性存储结构的一种,兼顾顺序表和链表的优点,是顺序表和链表的升级;静态链表的数据全部存储在数组中(顺序表),但存储的位置是随机的,数据直接的一对一关系是通过一个整型变量(称为“游标”,即数组下标,表示直接后继元素所在数组中的位置)维持。我感觉就像是一个包含两种数据的结构体。
  • 静态链表中,除了数据本身通过游标组成链表外,还需要有一条连接各个空闲位置的链表,称为备用链表。作用是回收数组中未使用或者之前使用过(现在不用)的存储空间,留待后期使用。
  • 一般情况下,cur(游标)为0的元素,作为备用链表的头结点,最后一个元素,也就是最大下标的元素,作为数据链表的头结点。
    在这里插入图片描述

通过存储的游标变量3,可以在a[3]中找到元素4的直接后继元素5;通过元素a[3]存储的游标变量6,可在在a[6]中找到元素5的直接后继元素6。

3.4循环链表
  • 将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表( circuar linkedlist)。
3.5双向链表
  • 双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

4 栈和队列

 栈(stack)是限定仅在表尾(栈顶)进行插入和删除操作的线性表。把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。栈又称为后进先出 (Last In First Out)的线性表,简称LIFO结构。

  • 栈的插入操作,叫作进栈(push),也称压栈、入栈。栈的删除操作,叫作出栈,也有的叫作弹栈(pop)。
4.1栈的顺序存储结构

 栈的顺序存储只准栈顶进出元素,所以不存在线性表插入和删除时需要移动元素的问题。不过它必须事先确定数组存储空间大小,万一不够用了,就需要编程手段来扩展数组的容量,非常麻烦。

  • 栈共享空间。
    在这里插入图片描述
    栈1有元素进栈,则先top1+1后给数组元素赋值;若栈2有元素进栈,则先top2-1后给数组元素赋值。只要它们俩不见面,两个栈就可以一直使用,两个栈见面之时,也就是两个指针之间相差1时,即 top1 + 1 == top2为栈满。
4.2栈的链式存储结构

栈的链式存储结构,简称为链栈。
在这里插入图片描述

4.3递归

 在高级语言中,调用自己和其他函数并没有本质的不同。我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数。
 简单的说,就是在前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压入栈中。在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层次中执行代码的其余部分,也就是恢复了调用的状态。

4.4 队列

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出 (First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
在这里插入图片描述

  • 队列的顺序存储结构
     front指针指向队头元素,rear 指针指向队尾元素的下一个位置,最大尺寸为 QueueSize。
  • 头尾相接的队列称为循环队列。
  • 队列的链式存储结构
     队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。

5 串

  • 串(string)是由零个或多个字符组成的有限序列,又名叫字符串。
  • 字符串也有顺序存储方式和链式存储,但串的链式存储结构除了在连接串与串操作时有一定方便之外,总的来说不如顺序存储灵活,性能也不如顺序存储结构好。
  • KMP匹配算法(查找字符串).

6 树

  • 树(Tree)是n(n=0)个结点的有限集。n=0时称为空树。在任意一棵非空树中:(1)有且仅有一个特定的称为根(Root)的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、……、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
  • 树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶结点(Leaf))或终端结点;度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。如图所示,因为这棵树结点的度的最大值是结点D的度,为3,所以树的度也为3。
  • 结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)。嗯,为什么不是父或母,叫双亲呢?呵呵,对于结点来说其父母同体,唯一的一个,所以只能把它称为双亲了。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根到该结点所经分支上的所有结点。
  • 结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层。若某结点在第Ⅰ层,则其子树的根就在第l+1层。其双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度(Depth)或高度。
6.1树的存储结构

图 6.1

  • 双亲表示法
    一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。
  • 孩子表示法
    把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。
  • 孩子兄弟表示法
    任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,设置两个指针,分别指向该结点的第一个孩子和右兄弟结点的存储地址。
6.2二叉树的定义

二叉树(Binary Tree)是n个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
二叉树的特点:

  • 每个结点最多有两棵子树,所以二叉树中不存在度大于2的结点。注意不是只有两棵子树,而是最多有。没有子树或者有一棵子树都是可以的。
  • 左子树和右子树是有顺序的,次序不能任意颠倒,即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。下图中,树1到树5是属于同一棵树,但它们却是不同的二叉树。
  • 所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。
  • 在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
  • 对一棵具有n个结点的二叉树按层序编号,如果编号为i(i=1,2,…n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
6.3二叉树的性质
  • 在二叉树的第i层上至多有 2 n − 1 2^{n-1} 2n1个结点( i ⩾ 1 i \geqslant 1 i1)。
  • 深度为k的二叉树上至多有 2 k − 1 2^k-1 2k1个结点( k ⩾ 1 k \geqslant 1 k1)。
  • 对于任何一颗二叉树,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
  • 具有n各个结点的完全二叉树的深度为[log2n]+1,[x]表示不大于x的最大整数。
  • 如果对一棵有n个结点的完全二叉树(其深度为[log2n]+1)的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),对任一结点i ( 1 ⩽ i ⩽ n 1 \leqslant i \leqslant n 1in)有:
     如果 i=1,则结点主是二叉树的根,无双亲;如果i>1,则其双亲是结点[i/2];如果2i>n,则结点i无左孩子(结点主为叶子结点);否则其左孩子是结点2i;如果2i+1>n,则结点1无右孩子;否则其右孩子是结点2i+1。
6.4二叉树的顺序存储结构
  • 二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置能反应出逻辑关系。
  • 把不存在的结点设置为“∧ ”,考虑一种极端的情况,一棵深度为k的右斜树,它只有k个结点,却需要分配2k-1个存储单元空间,这显然是对存储空间的浪费,顺序存储结构一般只用于完全二叉树。
6.5二叉链表
  • 二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。

    其中 data是数据域,khil和rchil都是指针域,分别存放指向左孩子和右孩子的指针。
6.6遍历二叉树
  • 二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
  • 二叉树的遍历有四种算法:前序遍历法、中序遍历、后序遍历、层序遍历。
6.7二叉树的建立

  • 如果要在内存中建立一个上图左边这样的树,为了能让每个结点确认是否有左右孩子,可以对它进行扩展,变成右边的样子,也就是将二叉树中每个结点的空指针引出一个虚结点,其值为一特定值,比如“#”。我们称这种处理后的二叉树为原二叉树的扩展二叉树。扩展二叉树就可以做到一个遍历序列确定一棵二叉树了。
6.8线索二叉树
  • 我们将指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树(Threaded Binary Tree)。利用线索二叉树可以充分利用空指针域。

    上图中,把这棵二叉树进行中序遍历后,将所有的空指针域中的rchil,改为指向它的后继结点。于是我们就可以通过指针知道H的后继是D(图中①),I的后继是B(图中②),」的后继是E(图中③),E的后继是A(图中④),F的后继是C(图中⑤),G的后继因为不存在而指向 NULL(图中⑥)。此时共有6个空指针域被利用。
6.9赫夫曼(Huffman)树

  • 从树中一个结点到另一个结点之间的分支构成两个结点之间的路径,路径上的分支数目称做路径长度。二叉树a中,根结点到结点D的路径长度就为4,二叉树b中根结点到结点D的路径长度为2。树的路径长度就是从树根到每一结点的路径长度之和。二叉树a 的树路径长度就为1+1+2+2+3+3+4+4;二叉树b的树路径长度就为1+2+3+3+2+1+2+2。
  • 如果考虑到带权的结点,结点的带权的路径长度为从该结点到树根之间的路径长度与结点上权的乘积。树的带权路径长度为树中所有叶子结点的带权路径长度之和。假设有n个权值{W1,W2…,Wk时},构造一棵有n个叶子结点的二叉树,每个叶子结点带权Wk,每个叶子的路径长度为lk,我们通常记作,则其中带权路径长度WPL最小的二叉树称做赫夫曼树
  • 二叉树a的WPL=5×1+15×2+40×3+30×4+10×4=315;
    二叉树b的 WPL=5×3+15×3+40×2+30×2+10×2=220。
  • 赫夫曼树的构造方法类似于赫夫曼编码,步骤挺多。。。不想写QWQ

7 图


 图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。

7.1 图的定义

对于图的定义,我们需要明确几个注意的地方:

  • 线性表中把数据元素叫元素,树中将数据元素叫结点,在图中数据元素则称之为顶点(Vertex)。
  • 线性表中可以没有数据元素,称为空表。树中可以没有结点,叫做空树。在图结构中,不允许没有顶点。在定义中,若V是顶点的集合,则强调了顶点集合V有穷非空
  • 线性表中,相邻的数据元素之间具有线性关系,树结构中,相邻两层的结点具有层次关系,而图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的。
  • 无向边:顶点之间的边没有方向,若任意两个顶点之间的边都是无向边,则该图为无向图。
  • 有向边:顶点之间的边有方向,若任意两个顶点之间的边都是有向边,则该图为有向图。有向边也称为弧,连接顶点A到D的有向边就是弧,A是弧尾,D是弧头,<A,D>表示弧。
  • 在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图
  • 有些图的边或弧具有与它相关的数字,这种与图的边或弧相关的数叫做(Weight)。这种带权的图通常称为(Network)。
  • 第一个顶点到最后一个顶点相同的路径称为回路或环(Cycle)。序列中顶点不重复出现的路径称为简单路径。除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路,称为简单回路简单环
  • 图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复叫简单路经。若任意两顶点都是连通的,则图就是连通图,有向则称强连通图。图中有子图,若子图极大连通则就是连通分量,有向的则称强连通分量。
  • 无向图中连通且n个顶点n-1条边叫生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向树。一个有向图由若干棵有向树构成生成森林。
7.2 图的存储结构

 图不可能用简单的顺序存储结构来表示。而多重链表的方式,即以一个数据域和多个指针域组成的结点表示图中的一个顶点,如果各个顶点的度数相差很大,按度数最大的顶点设计结点结构会造成很多存储单元的浪费,而若按每个顶点自己的度数设计不同的顶点结构,会带来操作的不便。下面是图的五种存储结构:

  • 邻接矩阵。图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。
  • 邻接表。数组与链表相结合的存储方式。顶点用一个一维数组存储,每个数据元素还需存储第一个邻接点的指针;然后每个顶点的所有邻接点构成一个线性表。
  • 十字链表。十字链表的好处就是因为把邻接表和逆邻接表整合在了一起,这样既容易找到以结点V为尾的弧,也容易找到以结点V为头的弧。
  • 邻接多重表。下图中, ivex 和jvex是与某条边依附的两个顶点在顶点表中下标。ilink指向依附顶点ivex的下一条边,jlink指向依附顶点jvex的下一条边,这就是邻接多重表结构。
  • 边集数组。边集数组是由两个一维数组构成。一个是存储顶点的信息;另一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标(begin)、终点下标(end)和权(weight)组成。
7.3图的遍历

 图的遍历是和树的遍历类似,从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历(Traversing Graph)。有两种遍历方法:

  • 深度优先遍历(DFS)。深度优先遍历其实就是一个递归的过程,它从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
    - 广度优先遍历(BFS)
7.4最小生成树

 构造连通网的最小代价生成树称为最小生成树(Minimum Cost Spanning Tree)。构造最小生成树有两种经典算法:普里姆算法和克鲁斯卡尔算法。

  • 普里姆(Prim)算法。假设N=(P,{E})是连通网,TE是N上最小生成树中边的集合。算法从U={uo}(uo∈V),TE={}开始。重复执行下述操作:在所有u∈U,v∈V-U的边(u,v)∈E中找一条代价最小的边(uo,vo)并入集合TE,同时vo并入U,直至U=V为止。此时 TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。
  • 克鲁斯卡尔(Kruskal)算法。假设N=(P,{E})是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图 T={V,{},图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。

 对比两个算法,克鲁斯卡尔算法主要是针对边来展开,边数少时效率会非常高,所以对于稀疏图有很大的优势;而普里姆算法对于稠密图,即边数非常多的情况会更好一些。

7.5最短路径

 对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。求解最短路径也有两种算法:迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd)算法。

  • 迪杰斯特拉(Dijkstra)算法。迪杰斯特拉算法是通过一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到想要的结果。
  • 弗洛伊德算法。对于有n个顶点的图,定义两个二维数组D[n][n]和P[n][n],D代表顶点到顶点的最短路径权值和的矩阵。P代表对应顶点的最小路径的前驱矩阵,就是每一行都是从0到n。在未分析任何顶点之前,我们将D命名为D-1,其实它就是初始的图的邻接矩阵。将Р命名为P-1,则有:D0 [v][w]= min { D-1 [v][w], D-1[v][0]+D-1 [0][w]}。接下来,在D0和P0的基础上继续处理所有顶点经过v1和v2后到达另一顶点的最短路径,得到D1和P1、D2和P2完成所有顶点到所有顶点的最短路径计算工作。

 弗洛伊德算法代码简洁,但用了三重循环嵌套,所以时间复杂度要高于迪杰斯特拉算法。如果需要求所有顶点至所有顶点的最短路径问题时,弗洛伊德算法应该是不错的选择。

  • 最小生成树能够保证整个拓扑图的所有路径之和最小,但不能保证任意两点之间是最短路径。最短路径是从一点出发,到达目的地的路径最小。
7.6 拓扑排序
  • 在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为 AOV 网(Activity On VertexNetwork )。
  • 在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为 AOE网(Activity On EdgeNetwork)。我们把 AOE 网中没有入边的顶点称为始点或源点,没有出边的顶点称为终点或者汇点。
  • AOV网是顶点表示活动的网,它只描述活动之间的制约关系,而AOE 网是用边表示活动的网,边上的权值表示活动持续的时间,把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。
  • 设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1,v2,…,vn,满足若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在顶点vj之前。则我们称这样的顶点序列为一个拓扑序列。
  • 拓扑排序,其实就是对一个有向图构造拓扑序列的过程。

 有向无环图时常应用于工程规划中,对于整个工程或系统来说,一方面关心的是工程能否顺利进行的问题,通过拓扑排序的方式,可以有效地分析出一个有向图是否存在环,如果不存在,那它的拓扑序列是什么?另一方面关心的是整个工程完成所必须的最短时间问题,利用求关键路径的算法,可以得到最短完成工程的工期以及关键的活动有哪些。

8 查找

  • 查找(Searching)就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。
  • 查找表(Search Table)是由同一类型的数据元素(或记录)构成的集合。
  • 关键字(Key)是数据元素中某个数据项的值,又称为键值。若此关键字可以唯一地标识一个记录,则称此关键字为主关键字;对于可以识别多个数据元素(或记录)的关键字,称为次关键字

查找表按照操作方式来分有两大种:静态查找表和动态查找表。

  • 静态查找表(Static Search Table):只作查找操作的查找表。主要操作有:(1)查询某个“特定的”数据元素是否在查找表中。
    (2)检索某个“特定的”数据元素和各种属性。
  • 动态查找表(Dynamic Search Table):在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。动态查找表的操作:(1)查找时插入数据元素。(2)查找时删除数据元素。
  • 顺序表查找。顺序查找(Sequential Search))又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给定值比较,若某个记录的关键字和给定值相等,则查找成功,找到所查的记录;如果直到最后一个(或第一个)记录,其关键字和给定值比较都不等时,则表中没有所查的记录,查找不成功。
  • 有序表查找
    折半查找(Binary Search)技术,又称为二分查找。它的前提是线性表中的记录必须是关键码有序(通常从小到大有序),线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。
    另一种有序表查找算法,插值查找法。插值查找(Interpolation Search)是根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法。
    斐波那契查找也是一种有序表查找。
  • 线性索引查找。数据结构的最终目的是提高数据的处理速度,索引是为了加快查找速度而设计的一种数据结构。索引就是把一个关键字与它对应的记录相关联的过程,一个索引由若干个索引项构成,每个索引项至少应包含关键字和其对应的记录在存储器中的位置等信息。
    稠密索引是指在线性索引中,将数据集中的每个记录对应一个索引项,,对于稠密索引这个索引表来说,索引项一定是按照关键码有序的排列。
    分块索引。稠密索引因为索引项与数据集的记录个数相同,所以空间代价很大。为了减少索引项的个数,我们可以对数据集进行分块,使其分块有序,然后再对每一块建立一个索引项,从而减少索引项的个数。分块有序,是把数据集的记录分成了若干块,并且这些块需要满足两个条件:块内无序,块间有序。对于分块有序的数据集,将每块对应一个索引项,这种索引方法叫做分块索引。
  • 倒排索引。记录号表存储具有相同次关键字的所有记录的记录号(可以是指向记录的指针或者是该记录的主关键字)。这样的索引方法就是倒排索引(inverted index)。
  • 二叉排序树。二叉排序树(Binary Sort Tree),又称为二叉查找树。它或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结构的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。二叉排序树插入操作是将关键字放到树中的合适位置。
  • 平衡二叉树,是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF (Balance Factor),距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树
    平衡二叉树构建的基本思想就是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
  • 多路查找树,每一个结点的孩子数可以多于两个,且每一个结点处可以存储多个元素。2-3树,2-3-4树,B树,B+树都是多路查找树。
  • 散列表查找(哈希表)概述。散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f (key)。查找时,根据这个确定的对应关系找到给定值 key的映射f (key),若查找集合中存在这个记录,则必定在f(key)的位置上。这种对应关系f称为散列函数,又称为哈希(Hash)函数。采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。散列技术既是一种存储方法,也是一种查找方法。散列技术最适合求解的问题是查找与给定值相等的记录。

9 排序

 假设含有n个记录的序列为{r1,r2…rn},其相应的关键字分别为{k1,k2…kn},需确定1,2,…, n的一种排列p1,p2…pn,使其相应的关键字满足kp1≤kp2≤…≤kpn(非递减或非递增)关系,即使得序列成为一个按关键字有序的序列{rp1,rp2…rpn},这样的操作就称为排序。

  • 内排序是在排序整个过程中,待排序的所有记录全部被放置在内存中。外排序是由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行。
  • 冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
  • 简单选择排序。通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第 i (1≤i≤n)个记录交换之。
  • 直接插入排序。直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中.从而得到一个新的、记录数增1的有序表。
  • 希尔排序
  • 堆排序。堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列了。
  • 归并排序。归并在数据结构中的定义是将两个或两个以上的有序表组合成一个新的有序表。归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到不小于n/2的最小整数)个长度为2或1的有序子序列;再两两归并,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
  • 快速排序。快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
  • 各种排序方法的时间复杂度
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值