【软考-数据库系统工程师-知识点】第三章 数据结构与算法

本文部分图片源自网络,仅供学习使用,侵删 (ง •_•)ง

数据结构:数据元素及元素间的相互关系和构造方法,结构就是元素之间的关系。

逻辑结构:元素之间的相互关系,分为线性结构和非线性结构。

存储结构:元素及元素之间关系的存储形式,分为顺序存储和链式存储。

3.1 线性结构

3.1.1 线性表

  1. 线性表的定义
  • 一个线性表是n个元素的有限序列(n≥0),通常表示为(a1,a1,…,an),其特点是在非空的线性表中:
    • 存在唯一的一个称作"第一个"的元素。
    • 存在唯一的一个称作"最后一个"的元素。
    • 除第一个元素外,序列中的每个元素均只有一个直接前驱。
    • 除最后一个元素外,序列中的每个元素均只有一个直接后继。
  1. 线性表的存储结构
  • 线性表的顺序存储
    • 指用一组地址连续的存储单元依次存储线性表中的数据元素,使逻辑上相邻的两个元素在物理位置上也相邻。
    • 在此方式下,元素间的逻辑关系无需占用额外的空间来存储。
    • 以LOC(a1)表示第一个元素的存储位置,L表示每个元素所占空间大小,则第i个元素的存储位置LOC(ai)=LOC(a1)+(n-1)*L。
    • 优点:可以随机存取表中的元素。按序号查找。
    • 缺点:插入和删除操作需要移动元素。插入平均移动(n/2)次,删除平均移动 [(n-1)/2]次。
  • 线性表的链式存储
    • 指用结点来存储数据元素,元素的结点地址可以连续,也可以不连续。

    • 在此方式下,存储数据元素的同时必须存储数据元素之间的逻辑关系。

    • 另外,结点空间只有在需要的时候才申请,无须事先分配。

    • 基本的结点结构:数据域用于存储数据元素的值,指针域用于存储当前元素的直接前驱或直接后继元素的位置信息。

      在这里插入图片描述

    • 线性表的分类

      线性链表(单链表):结点中只有一个指针域。

      双向链表:每个结点包含两个指针。

      循环链表:表尾结点的指针指向表中的第一个结点。

      静态链表:借助数组来描述线性表的链式存储结构。

3.1.2 栈和队列

  • 栈的定义及基本运算

    • 定义:栈是特殊的线性表,只能在其一端进行数据的检索和存储,这一端称为栈顶(top),另一端称为栈底(bottom)。

    • 运算队则:先进后出、后进先出。

    • 空栈:不含数据元素的栈。

    • 基本运算

      初始化栈 initStack(S):创建一个空栈S。

      判栈空 isEmpty(S):当栈为空时返回"真"值,否则返回"假"值。

      入栈 push(S):将元素x加入栈顶,并更新栈顶指针。

      出栈 pop(S):将栈顶元素从栈中删除,并更新栈顶指针。

      读栈顶元素 top(S):返回栈顶元素的值,但不修改栈顶指针。

  • 栈的存储结构

    • 栈的顺序存储:用一组地址连续的存储单元一次存储自栈顶到栈底的数据元素,设置栈顶指针。

      在此方式下,需要预定义或申请栈的存储空间。

      栈的空间容量是有限的,当一个元素入栈时,需要判断是否满栈。若栈满,则元素入栈会发生上溢现象。

    • 栈的链式存储:克服顺序存储的上溢问题。不必设置头结点,链表的头指针就是栈顶指针。

    • 栈的应用:表达式求值、括号匹配、计算机语言的实现、将递归过程转变为非递归过程的处理。

      后缀表达式的计算
      ①从左至右扫描后缀表达式;
      ②遇到运算对象,则压入栈中;
      ③遇到运算符,则从栈中弹出运算对象从右至左构成算式进行计算,并将运算结果压入栈中。
      ④重复以上过程,直至后缀表达式结束。

      中缀表达式转后缀表达式
      ①从左至右扫描中缀表达式;
      ②遇到运算对象,直接输出;
      ③遇到操作符:
      ​a.若是左括号,则压入栈中;
      b.若是右括号,则将栈中的操作符弹出并输出,直至遇到左括号为止;
      c.若是运算符,则根据优先级,依次将栈中比它优先级高或相等的运算符弹出并输出,然后将该运算符压入栈中。
      ④扫描完毕后,若栈中还有运算符,则依次弹出并输出。

  1. 队列
  • 队列的定义及基本运算
    • 定义:队列是特殊的线性表,只允许在队尾(rear)插入元素、在队头(front)删除元素。

    • 运算规则:先进先出、后进后出。

    • 队列的基本运算

      初始化队列 initQueue(Q):创建一个空的队列Q。

      判队空 isEmpty(Q):当队列为空时返回"真"值,否则返回"假"值。

      入队 enQueue(S):将元素x加入到队列Q的队尾,并更新队尾指针。

      出队deQueue(S):将元素x从队列Q删除,并更新队头操作。

      读队头元素 frontQueue(Q):读返回队头元素的值,但不更新队头指针。

  • 队列的存储结构
    • 队列的顺序存储:用一组地址连续的存储单元存放队列中的元素,设置队头指针和队尾指针。
    • 队列的链式存储:可设置头指针指向头结点,则队列为空的判定条件是头指针和尾指针的值相同,且均指向头结点。
    • 队列的应用:用于处理需要排队的场合,如操作系统中处理打印任务的打印队列。

3.1.3 串

  1. 串的定义
  • 定义:是仅由字符构成的有限序列,是取值范围受限的线性表。一般记为S='a1a2…an,S是串名,单引号括起来的是串值。
    • 串长:即串的长度,指字符串中的字符个数。

    • 空串:长度为0的串,空串不包括任何字符。

    • 空格串:由一个或多个空格组成的串。虽然空格是一个空白符,但也是一个字符,计算串长度时要计算在内。

    • 子串:由串中任意长度的连续字符构成的序列。

      含有字串的串称为主串,空串是任意串的子串。

      子串在主串中的位置是指子串首次出现时,该子串的第一个字符在主串的位置。

    • 串相等:指两个串的长度相等且对应位置上的字符也相同。

    • 串比较:从左向右依次比较,以同位置字符的ASCII码值较大者为大,若一个其中一个串先结束,则以串长者为大。

  1. 串的存储结构
  • 顺序存储:用一组地址连续的存储单元存储串值的字符序列。
  • 链式存储:每个结点中可存储一个字符,也可存储多个字符,需要考虑存储密度问题。
  • 通常情况下,字符串存储在一维字符数组中,每个字符串的末尾都有一个串结束符。
  1. 字符串运算
  • 赋值操作 StrAssign(s,t):将串t的值赋给串s。
  • 连接操作 Contact(s,t):将串t接续在串s的尾部,形成一个新串。
  • 求串长 StrLength(s):返回串s的长度。
  • 串比较 StrCompare(s,t):比较两个串的大小,返回值-1,0,1,分别表示s<t,s=t,s>t。
  • 求子串 SubString(s,start,len):返回串s中从start开始的、长度为len的字符序列。
  1. 串的模式匹配
  • 定义:子串(也称模式串)在主串中的定位操作通常称为串的模式匹配。
  • 基本的模式匹配算法(布鲁特—福斯算法)
    • 从主串的第一个字符起与模式串的第一个字符比较;
    • 若相等,则继续下一对字符的比较;否则从主串的第二个字符起与模式串的第一个字符重新开始比较;
    • 直至模式串中每个字符依次与主串中的一个连续的字符序列相等时为止。此时称为匹配成功;否则匹配失败。
    • 时间复杂度:最好情况(n+m)/2,最坏情况m(n+m)/2。
  • 改进的模式匹配算法(KMP算法)
    • 每当过程中出现相比较的字符不相等时,不需要回溯主串字符的位置指针;
    • 而是利用已经得到的"部分匹配"的结果,将模式串向后"滑动"尽可能远的距离,再继续进比较。
    • 时间复杂度O(n+m)。

3.2 数组和矩阵

3.2.1 数组

  1. 数组的定义及基本运算
  • 数组的定义:一维数组是长度固定的线性表,数组中的每个元素类型相同。

    n维数组是定长线性表在维数上的扩张,即线性表的元素又是一个线性表。

  • 数组的特点

    • 数据元素数目固定,一旦定义了一个数组结构,就不再有元素的增减变化。
    • 数据元素具有相同的类型。
    • 数据元素的下标关系具有上下界的约束且下标有序。
  • 数组的操作

    • 取值操作。给定一组下标,读取对应的数据元素。
    • 赋值操作,给定一组下标,存储或修改与其对应的数据元素。
  1. 数组的顺序存储
  • 数组一般不作插入和删除运算,一旦定义则结构中的数据元素个数和元素之间的关系就不再变动,故采用顺序存储。
  • 数据元素的位置是其下标的线性函数。设每个数据元素占用L个单元,m、n为数据的行数和列数:
    • 以行为主序优先存储的地址计算公式:LOC(Aij) = A11+( ( i - 1 ) × n + ( j - 1 ) ) × L。
    • 以列为主序有限存储的地址计算公式:LOC(Aij) = A11+( ( j - 1 ) × m + ( i - 1) ) × L。

3.2.2 矩阵

  • 压缩存储:为多个值相同的元素只分配一个存储单元,对零元素不分配存储单元。
  • 特殊矩阵
    • 对称矩阵:矩阵A(n×n)中的元素存在 Aij = Aji 的特点。
    • 三角矩阵:矩阵中的元素在主对角线以上或以下(不包含主对角线)都是零。
    • 对角矩阵:矩阵中的非零元素都集中在以主对角线为中心的带状区域。
  • 稀疏矩阵
    • 稀疏矩阵的含义:在一个矩阵中,非零元素的个数远远少于零元素的个数,且非零元素的分布没有规律。
    • 对于稀疏矩阵,存储非零元素必须存储其位置(即行号和列号),所以三元组(i,j,Aij)可唯一确定矩阵中的一个元素。
    • 一个稀疏矩阵可由表示非零元素的三元组及其行数和列数唯一确定。
    • 稀疏矩阵的三元组表构成一个线性表,其顺序存储结构称为三元组顺序表,其链式存储结构称为十字链表。

3.3 树和图

3.3.1 树

  1. 树的定义
  • 树:是n(n≥0)个结点的有限集合。当n=0时称为空树。
  • 根:在任一非空树(n>0)中,有且仅有一个称为根的结点。
  • 子树:除根结点外,其余结点可分为m(m≥0)个互不相交的有限集T1,…,Tm,其中每个集合都是一棵树,称为根结点的子树。
  • 树中元素之间有明显的层次关系,对于树中某结点:最多只和上一层的一个结点有直接关系,而与下一层多个结点有直接关系。
  • 双亲、孩子和兄弟
    • 结点的子树的根称为该结点的孩子。相应的该结点称为其子结点的双亲。
    • 具有相同双亲的结点互为兄弟。
  • 结点的度:一个结点的子树的个数记为该结点的度。
  • 叶子结点:也称终端结点,指度为0的结点。
  • 内部结点:度不为零的结点称为分支结点或非终端结点。除根结点外,分支结点也称为内部结点。
  • 结点的层次:根为第一层,根的孩子为第二层,以此类推。
  • 树的高度:一个树的最大层次数记为树的高度(或深度)。
  • 有序(无序)树:若将树中结点的各子树看成是从左到右具有次序的,即不能交换,则称该树为有序树,否则称为无序树。
  • 森林:m(m≥0)棵互不相交的树的集合。
  1. 二叉树的定义
  • 二叉树的定义
    • 二叉树是n(n≥0)个结点的有限集合:或是空树,或是由一个根结点及两棵(左子树、右子树)互不相交二叉树所组成。
  • 二叉树与树的区别
    • 二叉树中的子树要区分左子树和右子树,即便只有一颗子树也要明确指出是左子树还是右子树。
    • 二叉树中结点的最大度为2,而树中不限制结点的度数。
  1. 二叉树的性质
  • 二叉树第 i(i≥1)层上至多有 2^(i-1) 个结点。

  • 深度为 k(k≥1)的二叉树至多有 (2^k) - 1个结点。

  • 对任何一棵二叉树,若其终端结点树为N0,度为2的结点数为N2,则N0 = N2 + 1。

    • 设二叉树共有N个结点,E条边,Ni表示度为 i 的结点的个数,则:

      N = E + 1①,可理解为除根节点外,每个结点都有一条线牵着。

      E = (N0 × 0)+(N1 × 1)+(N2 × 2)②,可理解为度与边是1:1,即每个结点的度即为其向下引出的边。

      由①②可得,N0 = N2 + 1。

  • 具有n个结点的完全二叉树的深度为 [ log_2(n) + 1 ]。

    • 满二叉树:若深度为 k 的二叉树有 2^k -1 个结点,则称其为满二叉树。

    • 满二叉树中结点编号规则:从根结点起,自上到下、自左到右依次进行。编号为 i 的结点的左孩子编号为2i,右孩子为2i+1。

    • 完全二叉树:每一个结点都与深度与其相同的满二叉树中编号为 1~n 的结点一一对应。

      在一个高度为 h 的完全二叉树中,除了第 h 层(即最后一层),其余各层都是满的。

      在第 h 层的结点必须从左到右依次放置,不能留空。

  1. 二叉树的存储结构
  • 二叉树的顺序存储结构
    • 用一组地址连续的存储单元存储二叉树中的结点。

      必须把结点排成一个适当的线性序列,并且结点在这个序列中的相互位置能反映出结点之间的关系。

    • 对于完全二叉树,假设有编号为 i 的结点,则:

      若 i = 1,则该结点为根结点,无双亲;若 i > 1,则该结点的双亲结点为[ i / 2 ]。

      若 2i ≤ n,则该结点的左孩子编号为 2i,否则无左孩子。

      若 2i + 1 ≤ n,则该结点的右孩子编号为 2i,否则无右孩子。

    • 在顺序存储结构中,以结点在存储单元中的位置来表示结点之间的关系。一般二叉树也必须按照完全二叉树的形式存储。

  • 二叉树的链式存储结构:使用二叉链表或三叉链表来存储存储二叉树。链表的头指针指向树的根结点。
  1. 二叉树的遍历
  • 遍历的含义:按照某种策略访问树中的每个结点,且仅访问一次。
  • 遍历的分类
    • 先序遍历:依次访问根结点、左子树、右子树。
    • 中序遍历:依次访问左子树、根结点、右子树。
    • 后序遍历:依次访问左子树、右子树、根结点。
    • 层序遍历:自上而下、自左至右逐层访问树中各层结点。
  1. 最优二叉树
  • 最优二叉树:也称哈夫曼树,是一类带权路径长度最短的树。
  • 路径:从树中一个结点到另一个结点之间的通路称为结点间的路径。该通路上分支树木称为路径长度。
  • 树的路径长度:从树根到每一个叶子之间的路径长度之和。
  • 结点的带权路径长度:从该结点到树根之间的路径长度与该结点权的乘积。
  • 树的带权路径长度(Weighted Path Length of Tree,WPL):树中所有叶子结点的带权路径长度之和。
  • 构造最优二叉树的哈夫曼方法
    • 根据给定的n个权值{W1,…,Wn}构成n棵二叉树的集合F={T1,…,Tn},其中每棵树Ti中只有一个带权为Wi的根结点,其左右子树均空。
    • 在F中选取两棵根结点的权值最小的树作为左右子树,构造一棵新的二叉树,置新构造二叉树的根结点的权值为左、右子树根结点的权值之和。
    • 从F中删除这两棵树,同时将新得到的二叉树加入F中。
    • 重复第2、第3步,直到F中只含一棵树时为止,这棵树便是最优二叉树(哈夫曼树)。
  1. 二叉查找树
  • 二叉查找树:也称二叉排序树。它或是空树,或是具有如下性质的二叉树
    • 若它的左子树非空,则左子树上的所有结点的关键码均小于根结点的关键码值。
    • 若它的右子树非空,则右子树上的所有结点的关键码均大于根结点的关键码值。
    • 左、右子树本身就是两棵二叉查找树。
  • 对二叉树进行中序遍历,可得到一个关键码有序递增的结点序列。

3.3.2 图

  1. 图的定义及术语
  • 图G是由两个集合V和E构成的二元组,记作G=(V,E),其中V是图中顶点的非空有限集合,E是图中边的有限集合。

  • 在图中,数据结构中的数据元素用顶点表示,数据元素之间的关系用边表示。

  • 有向图:图中的每条边都是有方向的,从顶点Vi到Vj的有向边<Vi,Vj>称为弧,Vi称为弧尾,Vj称为弧头。

  • 无向图:图中的每条边都是无方向的,顶点Vi到Vj之间的边用(Vi,Vj)表示。

  • 完全图:含有n个结点的图,图中每一个顶点与其他(n-1)个顶点之间都有边。

    含有n个结点的无向完全图共有n(n-1)/2条边。

    含有n个结点的有向完全图共有n(n-1)条边。

  • 度、出度和入度

    • 度:顶点v的度是指关联于该顶点的边的数目,记作D(v)。

    • 若G为有向图

      顶点的度:表示该顶点的入度和出度之和。

      顶点的入度:是以该顶点为终点的有向边的数目。

      顶点的出度:是以该顶点为起点的有向边的数目。

  • 路径

    • 在无向图中,从顶点Vp到顶点Vq的路径是指存在一个顶点序列Vp,Vi1,…Vin,Vq,使得(Vp,Vi1),…,(Vin,Vq)均属于E(G)。
    • 在有向图中,其路径也是有 方向的,它E(G)中的有向边<Vp,Vi1>,…,<Vin,Vq>组成。
    • 路径长度:是路径上边或弧的数目。
    • 回路(环):第一个顶点和最后一个顶点相同的路径。
    • 简单路径:除了起点Vp和终点Vq可以相同外,其余顶点均不相同的路径。
  • 子图:若有两个图G=(V,E)和G’=(V’,E’),如果V’⊆V且E’⊆E,则称G’为G的子图。

  • 连通图:在无向图G中,若从顶点Vi到Vj有路径,则称顶点Vi和顶点Vj是连通的。任意两个顶点都连通的无向图是连通图。

  • 强连通图:在有向图G中,对于每一对顶点Vi,Vj∈V且Vi≠Vj,从顶点Vi到顶点Vj和从顶点Vj到顶点Vi都存在路径。

  • 网:边(或弧)具有权值的图称为网。

  1. 图的存储结构
  • 邻接矩阵表示法

    • 对于具有n个顶点的图G=(V,E)来说,其邻接矩阵是一个n阶方阵,且满足A[i][j]=

      1,表示(Vi,Vj)或<Vi,Vj>是E中的边;

      0,表示(Vi,Vj)或<Vi,Vj>不是E中的边。

    • 无向图的邻接矩阵是对称的,而有向图的邻接矩阵则不一定。

    • 对于无向图,顶点Vi的度是邻接矩阵中第i行(或列)的值不为0的元素之和。

    • 对于有向图,第i行的元素之和为顶点Vi的出度,第j列的元素之和为顶点Vj的入度。

    • 网(赋权图)的邻接矩阵可定义为A[i][j]=

      Wij,表示(Vi,Vj)或<Vi,Vj>是E中的边;

      ∞,表示(Vi,Vj)或<Vi,Vj>不是E中的边。

  • 邻接链表表示法

    • 为图的每一个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对于有向图是以Vi为尾的弧)。

    • 邻接链表中的结点有表结点和表头结点两种类型
      在这里插入图片描述

      adjvex:指示与顶点Vi邻接的顶点的序号。

      nextarc:指示下一条边或弧的结点。

      info:存储和边或弧有关的信息,如权值。

      data:存储顶点Vi的名或其他有关信息。

      firstarc:指示链表中第一个结点。

    • 对于有n个顶点、e条边的无向图来说,其邻接链表需要n个头结点和2e个表结点。

    • 对于无向图的邻接链表,顶点Vi的度恰为第i个邻接链表中表结点的数目。

    • 对于有向图的邻接链表,第i个邻接链表中表结点的数目只是顶点Vi的出度。

3.4 常用算法

3.4.1 算法概述

  1. 算法的基本概念
  • 算法的含义:是问题求解过程的精确描述,它为解决某一特定类型的问题规定了一个运算过程。
  • 算法的特性
    • 有穷性:即执行步骤有限,且每一步的执行时间有限。
    • 确定性:每一步都是确切定义、无歧义的。
    • 可行性:所要进行的运算都能够由相应的运算装置所理解和实现,并可通过有穷次运算完成。
    • 输入:一个算法有零个或多个输入。
    • 输出:一个算法有一个或多个输出。
  • 算法的评价
    • 正确性(有效性):对任何合法的输入,算法都能得到正确的结果。
    • 可读性:指算法可被理解的难易程度。
    • 健壮性:即对非法输入的抵抗能力。
    • 效率:算法运行所花费的时间和使用的空间。
  1. 算法与数据结构
  • 数据结构:指定数据的类型和数据的组织形式。
  • 算法:描述操作的步骤。
  • 程序 = 数据结构 + 算法。
  1. 算法的描述
  • 流程图。
    • 加工步骤,用方框表示。
    • 逻辑条件,用菱形表示。
    • 控制流,用箭头表示。
  • N/S盒图。
    • 每个处理步骤用一个盒子表示,盒子可以嵌套。
    • 对于每个盒子,只能从上面进入,从下面走出,除此之外别无其他出入口。
    • 盒图限制了随意的转移控制,保证了程序的良好结构。
  • 伪代码。
    • 特点:借助于程序语言的语法结构和自然语言叙述,使算法具有良好的结构又不拘泥于程序语言的限制。
    • 这样的算法容易读写,而且容易转换成程序。
  • 决策表。
    • 一种图形工具,将比较复杂的决策问题简洁、明确、一目了然地描述出来。
  1. 算法的效率
  • 常采用算法的时空开销随问题规模n的增长趋势来表示其时空复杂度。
  • T(n)是算法运行所消耗时间或存储空间的总量。当n增大到一定值后,T(n)中受影响最大的是n的幂次最高的项,则T(n)=O(f(n))。
  • 基本语句:执行不可分割的语句。
  • 语句频度:指语句被重复执行的次数,即对于某个基本语句,若在算法的执行过程中被执行n次,则其语句频度为n。
  • 算法中各基本语句的语句频度之和表示算法的执行时间。

3.4.2 排序

  1. 排序基础知识
  • 排序
    • 假设含n个记录的文件内容为{R1,R2,…,Rn},其相应的关键字为{K1,k2,…,Kn}。
    • 经过排序确定一种排列{Rj1,Rj2,…,Rjn},它们的关键字满足如下递增(减)关系:Kj1 ≤ Kj2 ≤ … ≤ Kjn(或Kj1 ≥ Kj2 ≥ … ≥ Kjn)
  • 排序的稳定性
    • 稳定排序:关键字相同的Ri和Rj排序前和排序后的相对次序保持不变。
    • 不稳定排序:关键字相同的Ri和Rj排序前和排序后的相对次序可能不同。
  • 内部排序和外部排序
    • 内部排序:待排序记录全部存放在内存中进行排序的过程。
    • 外部排序:待排序记录的数量很大,以至内存不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
  • 在排序过程中的两种基本操作
    • 比较两个关键字的大小。
    • 将记录从一个位置移动到另一个位置。
  1. 简单排序
  • 直接插入排序:稳定,时间复杂度为O(n^2),空间复杂度O(1)。

    在这里插入图片描述

  • 冒泡排序:稳定,时间复杂度为O(n^2),空间复杂度O(1)。

    在这里插入图片描述

  • 简单选择排序:不稳定(2a,2b,1会变成1,2b,2a),时间复杂度为O(n^2),空间复杂度O(1)。

    在这里插入图片描述

  1. 希尔排序
  • 含义:又称为"缩小增量排序",是对直接插入排序方法的改进。
  • 基本思想
    • 先将整个待排记录分割成若干子序列,然后分别进行直接插入排序。
    • 待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。
  • 具体做法
    • 取d1(<n)作为第一个增量,把全部记录分成d1组,将距离为d1倍数的记录放在同一组中,在各组内进行直接插入排序。
    • 取第二个增量d2(d2<d1),重复上述分组和直接插入排序过程,依此类推,直至所取的增量di = 1 (di<d(i-1)<…<d1)。
      在这里插入图片描述
  • 特点:不稳定,存在不相邻记录的交换。
  1. 快速排序
  • 基本思想
    • 通过一趟排序将待排的记录划分为独立的两个部分,称为前半区和后半区。
    • 其中,前半区中记录的关键字均不大于后半区记录的关键字。
    • 然后再分别对这两部分记录继续进行快速排序,从而使整个序列有序
  • 具体做法
    • 分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=1),指向数字6。让哨兵j指向序列的最右边(即j=10),指向数字8。
      在这里插入图片描述

    • 首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,哨兵j一步一步向左挪动(即j––),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。
      在这里插入图片描述
      现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下:

      6 1 2 5 9 3 4 7 10 8

    • 到此,第一次交换结束。接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。
      在这里插入图片描述
      此时再次进行交换,交换之后的序列如下:

      6 1 2 5 4 3 9 7 10 8

    • 第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。
      在这里插入图片描述
      我们将基准数6和3进行交换。交换之后的序列如下:

      3 1 2 5 4 6 9 7 10 8

    • 到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。现在基准数6已经归位,它正好处在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列。
      在这里插入图片描述

  • 特点
    • 不稳定,时间复杂度为O(nlogn)。
    • 在所有算法复杂度为此数量级的排序方法中,快速排序被认为是平均性能最好的一种。
  1. 堆排序
  • 堆的定义:对于n个元素的关键字序列{k1,k2,…,kn},当且仅当满足下列关系时称其为堆。
    在这里插入图片描述
  • 堆排序基本思想
    • 对一组待排序记录的关键字,首先把它们按堆的定义排成一个序列(即建立初始堆),从而输出堆顶的最小/大关键字。
    • 然后将剩余的关键字再调整成新堆,便得到次小/大的关键字。
    • 如此反复,直到全部关键字排成有序序列为止。
  • 堆排序特点
    • 不稳定,时间复杂度为O(nlogn),空间复杂度为O(1)。
  1. 归并排序
  • 归并的含义:将两个或两个以上的有序文件合并成为一个新的有序文件。
  • 归并排序基本思想
    • 把一个有n个记录的无序文件看作由n个长度为1的有序子文件组成,然后进行两两归并,得到n/2个长度为2或1的有序文件;
    • 再两两归并,如此重复,直至最后形成包含n个记录的有序文件为止。
      在这里插入图片描述
  • 归并排序特点
    • 稳定,时间复杂度为O(nlogn),空间复杂度为O(n)。
  1. 内部排序方法小结
    排序方法最好时间平均时间最坏时间辅助空间稳定性
    直接插入排序O(n)O(n^2)O(n^2)O(1)稳定
    冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
    简单选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
    希尔排序——O(n^1.25)——O(1)不稳定
    快速排序O(nlogn)O(nlogn)O(n^2)O(nlogn)~O(n)不稳定
    堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
    归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
  • 选择排序方法时需要考虑的主要因素
    • 待排序的记录个数n。
    • 记录本身的大小。
    • 关键字的分布情况。
    • 对排序稳定性的要求。
    • 语言工具的条件。
    • 辅助空间大小。
  • 结论
    • 若待排序的记录数目n较小时,可采用插入排序和简单选择排序。由于直接插入排序所需的记录移动操作较简单选择排序较多,因此当记录本身信息量较大时,用简单选择排序方法较好。
    • 若待排序记录按关键字基本有序,则宜采用直接插入排序或冒泡排序。
    • 若n较大,则应采用时间复杂度为O(nlogn)的排序方法,例如快速排序、堆排序或归并排序。
    • 快速排序在目前内部排序方法中被认为是最好的方法,当待排序的关键字随机分布时,快速排序的平均时间最短。
    • 堆排序只需要一个辅助内存空间,并且不会出现在快速排序中可能出现的最坏情况。
  1. 外部排序
  • 含义:是对大型文件的排序,待排序的记录存放在外存。
  • 特点:在排序的过程中,内存只存储文件的一部分记录。整个排序过程需要进行多次内外存间的数据交换。
  • 常用:常用的外部排序方法是归并排序。

3.4.3 查找

  1. 查找表以及查找效率
  • 查找表:指由同一种类型的数据元素(或记录)构成的集合。分为静态查找表和动态查找表。
  • 静态查找表:对查找表经常要进行的两种操作如下:
    • 查询某个"特定"的数据元素是否在查找表中。
    • 检索某个"特定"的数据元素的各种属性。
  • 动态查找表:对查找表经常要进行的另外两种操作如下:
    • 在查找表中插入一个数据元素。
    • 在查找便中删除一个数据元素。
  • 关键字:是数据元素(或记录)的某个数据的值,用它来识别(标识)这个数据元素(或记录)。
    • 主关键字:指能唯一标识一个数据元素的关键字。
    • 次关键字:指能标识多个数据元素的关键字。
  • 查找:根据给定的某个值,在查找表中确定是否存在一个其关键字等于给定值的记录或数据元素。
  • 平均查找长度(Average Search Length,ASL):关键字和给定值进行比较的记录个数的平均值。
    在这里插入图片描述
  1. 顺序查找
  • 含义:从表的一端开始,逐个进行记录的关键字和给定值的比较。
  • ASL= (1+…+n)/n = (n+1)/2。
  • 优点:算法简单且适应面广,对查找表的结构没有要求,无论记录是否按关键字有序排列均可应用。
  • 缺点:在n值较大时,其平均查找长度较大,查找效率较低。
  1. 折半查找/二分查找
  • 基本思想
    • 先令查找表中间位置记录的关键字和给定值比较。
    • 若相等,则查找成功;
    • 若不等,则缩小范围,直至新的查找区间中间位置记录的关键字等于给定值或者查找区间没有元素时(即查找不成功)为止。
  • ASL = (n+1)/n · log(n+1) - 1,n较大时,ASL趋近于log(n+1) - 1。
  • 优点:比顺序查找的效率高,适用于表不易变动,且又经常进行查找的情况。
  • 缺点:要求查找表顺序存储并且按关键字有序排列。
  1. 索引顺序查找/分块查找
  • 基本思想
    • 将长度为n的表均匀地分成b块,每块中含有s个记录,即 b = n / s。
    • 每一块中的关键字不一定有序,但块之间是有序的,即后一块所有记录的关键字均大于前一块中的最大关键字。
    • 建立"索引表",索引表按关键字有序。
    • 查找第一步,在索引表中确定待查记录所在块;
    • 查找第二步,在块内顺序查找。
  • ASL = (b+1)/2 + (s+1)/2 = (n/s+s)/2+1。
    • 其平均查找长度不仅和表长n有关,还与每一块内的记录数s有关。
    • 当s取√n时,ASL取最小值(√n+1),此时的查找效率比顺序查找要好得多,但远不及折半查找。
  1. 树表查找
  • 二叉查找表、B_树、红黑树等是常见的以树表方式组织的查找表。

  • 二叉查找树:是一种动态查找表,其特点是表结构本身是在查找过程中动态生成的。

  • B_树(Balance,意为平衡):一颗m阶的B_树,或为空树,或为满足下列特性的m阶树:

    • 树中每个结点最多有m棵子树。

    • 若根结点不是叶子结点,则最少有两棵子树。

    • 除根之外的非终端结点最少有[m/2]棵子树。

    • 所有非终端结点种包含下列数据信息:(n,A0,K1,A1,…,Kn,An)

      Ki(i=1,2,…,n)为关键字,且Ki<K(i+1)

      Ai(i=0,1,…,n)为指向子树根结点的指针,且指针A(i-1)所指子树中所有结点的关键字均小于Ki。

      An所指子树中所有结点的关键字均大于Kn。

      n为结点中关键字的个数。

    • 所有叶子结点都出现在同一层次上,并且不带信息。

      可以看作是外部结点或查找失败的结点,实际上这些结点不存在,质量这些结点的指针为空。

  • B_树的插入和删除运算较为复杂,因为要保证运算后结点中关键字的个数≥[m/2]-1,因此涉及结点的"分裂"和"合并"问题。

  1. 哈希查找
  • 哈希表的组织思想:要求记录的关键码与其存储位置之间一一对应,通过这个关系,能很快地由关键码找到记录。
  • 哈希造表
    • 将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的"像"作为记录在表中的存储位置。

      这种表称为哈希表。

      这一映射过程称为哈希造表或散列。

      所得的存储位置称为哈希地址或散列地址。

    • 在构造哈希表时,是以关键字为自变量计算一个函数(称为哈希函数)来得到该记录的存储地址并存入元素。

  • 处理冲突
    • 冲突:对于某个哈希函数Hash和两个关键字K1和K2,如果K1≠K2而Hash(K1)=Hash(K2),则称为出现了冲突。

      对该哈希函数来说,K1和K2称为同义词。

    • 解决冲突:为出现冲突的关键字找到另一个"空"的哈希地址。

    • 解决冲突常见用方法:开放定址法、链地址法/拉链法、再哈希法、建立公共溢出区法。

    • 在处理冲突的过程中,可能得到一个地址序列,记为Hi(i=1,2,…,k)。

    • 开放定址法:Hi = (Hash(key)+di)%m(i=1,2,…,k)(k≤m-1)。

      其中,Hash(key)为哈希函数;m为哈希表的表长;di为增量序列。增量序列有如下三种:
      ① di = 1,2,3,…,m-1,称为线性探测再散列。
      ② di = 12,-12,22,-22,…,±k^2(k ≤ m/2),称为二次探测再散列。
      ③ di = 伪随机序列,称为随机探测再散列。

    • 链地址法:将具有相同哈希函数值的记录组织成一个链表,当链域的值为NULL时,表示已没有后继记录。

  • 哈希查找
    • 在线性探测法解决冲突的方式下,进行查找有两种可能:

      第一种情况是在某一位置上查到了关键字等于key的记录,查找成功;

      第二种情况是按探测序列查不到关键字为key的记录而又遇到了空单元,表明元素不在表中,查找失败。

    • 在用链地址法解决冲突构造的哈希表中查找元素:根据哈希函数得到元素所在链表的头指针,然后在链表中进行顺序查找。

3.4.4 递归算法

  • 递归(recursion)用于解决以下类型的问题
    • 可归纳描述的问题。
    • 可分解为结构自相似的问题,即构成问题的部分与问题本身在结构上相似。
  • 结构自相似问题的特点
    • 整个问题的解决可以分为两部分

      第一部分是一些特殊或基本的情况,可直接解决;

      第二部分与原问题相似,可用类似的方法解决,但比原问题的规模小。

    • 每次递归时第二部分的规模都在缩小,最终缩小为第一部分的情况结束递归。

3.4.5 图的相关算法

  1. 求最小生成树算法
  • 生成树的概念
    • 设图G=(V,E)是一个连通图,如果其子图是一棵包含G的所有顶点的树,则该子图称为G的生成树(Spanning Tree)。
    • 对于有n个顶点的连通图,至少有n-1条边,而生成树中恰好有n-1条边,所以连通图事务生成树是该图的极小连通子图。
    • 若在图的生成树中任意加一条边,则必然形成回路。
    • 图的生成树并不唯一。
  • 最小生成树
    • 概念:把生成树各边的权值总和称为生成树的权,把权值最小的生成树称为最小生成树。

    • 普里姆(Prim)算法

      以一个顶点集合U={u0}为初态,不断寻找与U中顶点相邻且代价最小的边的另一个顶点。

      扩充集合U直至U中包含图中所有顶点。

    • 克鲁斯卡尔(Kruskal)算法:时间复杂度O(eloge)(e为网中的边数),与图中顶点数无关,适于求边稀疏的网的最小生成树。

      令最小生成树的初态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。

      在图中选择代价最小的边,若该边依附的顶点落在T中不同连通分量上,则将此边加入T,否则寻找下一条代价最小边。

      依次类推,直至T中所有顶点都在同一连通份量上为止。

  1. 拓扑排序
  • AOV网(Activity On Vertex network)
    • 概念:用顶点表示活动,用有向边表示活动之间的优先关系,称此类有向图为以顶点表示活动的网(AOV网)。
    • AOV网中不应该出现有向环,即某项活动不能以自身任务的完成为先决条件。
    • 检测AOV网中是否存在回路的方法是对其进行拓扑排序。
  • 拓扑排序
    • 概念:若在AOV网中从Vi到Vj有一条路径,则在拓扑排序中,顶点Vi必然在顶点Vj之前。

    • 排序过程

      在AOV网中选择一个入度为零(没有前驱)的顶点且输出它;

      从网中删除该顶点以及与该顶点有关的所有边;

      重复上述两步,直至网中不存在入度为零的顶点位置。

    • 排序结果

      一种是所有顶点已输出,此时整个拓扑排序完成,说明网中不存在回路。

      另一种是尚有未输出的顶点,剩余顶点均有前驱顶点,表明网中存在回路,拓扑排序无法进行下去。

  1. 求单源点的最短路径算法
  • 单源点的最短路径:指给定带权有向图G和源点V0,求从V0到G中其余各顶点的最短路径。
  • 迪杰斯特拉(Dijkstra)提出了按路径长度递增的次序产生最短路径的算法,其思想如下:
    • 把网中所有的顶点分为两个集合S和T,S集合的初态只包含顶点V0,T集合的初态包含除V0之外的所有顶点。
    • 凡以V0为源点,已经确定了最短路径的终点并入S集合中。
    • 按各顶点与V0间的最短路径长度递增的顺次序,逐个把集合T中的顶点加入到S集合中去。
      在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值