1.1基本概念和术语

 

数据:是描述客观事物和符号,是计算机中可以操作的对象,是以被计算机识别,并输入给计算机处理的符号集合。

  • 数据元素:是组成数据的、有一定意义的基本单位,在计算机通常作为整体处理。也被称为记录;
  • 数据项:一个数据元素可以由若干个数据项组成(数据项是数据不可分割的最小单位);
  • 数据对象:是性质相同的数据元素的集合,是数据的子集;
  • 数据结构:数据元素之间不是独立的,而是存在特定的关系,我们将这些关系称为结构(是相互之间存在一种或多种特 定关系的数据元素的集合。

 

1.2逻辑结构和物理结构

逻辑结构:是指数据对象中数据元素之间的相互关系。

  • 集合结构:集合结构中的数据元素除了同属于一个集合外,它们之间没有其他关系;
  • 线性结构:线性结构的中的数据元素之间是一对一的关系;
  • 树形结构:树形结构中的数据元素之间存在一种一对多的层次关系;
  • 图形结构:图形结构的数据元素是多对多的关系。

物理结构: 是指数据的逻辑结构在计算机中的存储形式。(顺序存储和链式存储)。

  1. 顺序存储结构:是把数据元素存放在地址连续的存储单位里,其数据间的逻辑关系和物理关系一致的;
  2. 链式存储结构:是把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。

总结:逻辑结构是面向问题的,而物理结构是面向计算机的,其基本的目标就是将数据及其逻辑关系存储到计算机的内存中。

 

           1.3抽象数据类型

数据类型:是指一组性质相同的值的集合及定义在此集合上的一些操作的总称。

  • 原子类型:是不可以再分解的基本类型,包括整型、实型、字符型等;
  • 结构类型:由若干个类型组合而成,是可以再分解的。例如整型数组是由若干整型数据组成的。

抽象是指抽取出事物具有的普遍性的本质。

抽象数据类型:是指一个数学模型及定义在该模型上的一组操作。

抽象数据类型体现了程序设计中问题分解、抽象和信息隐藏的特性。

 


 

 

  2.1 算法定义

          算法:是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。

        

 2.2 算法的特性

          算法具有五个基本特性:输入,输出,有穷性,确定性和可行性。

  •  输入输出:算法具有零个或多个输入,至少有一个或多个输出。
  • 有穷性:指算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。
  • 确定性:算法的每一步骤都具有确实的含义,不会出现二义性。
  • 可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。

 2.3算法设计的要求

  • 正确性:算法的正确性是指算法至少应该具有输入,输出和加工处理无歧义性,能正确反映问题的需求,能够得到问题的正确答案。
  • 可读性:算法设计的另一目的是为了便于阅读,理解和交流。
  • 健壮性:当输入数据不合法时,算法 也能做出相关处理,而不是产生异常或莫名其妙的结果。
  • 时间效率高和存储量低:设计算法应该尽量满足时间效率高和存储量低的需求。

2.4算法效率的度量方法

  • 事后统计方法:这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制和程序的运行时间进行比较,从而确定算法效率的高低。
  • 事前分析估算方法:在计算机程序编制前,依据统计方法对算法进行估算。

 

 

  2.5算法时间复杂度

  • 算法时间复杂度定义:在进行算法分析时,语句总的执行次数 T(n)是关于问题规模n的函数,进而分析T(n)随 n 的变化情况并确定 T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作:T(n)= O (f(n))。它表示随问题规模 n 的增大,算法执行时间的增长率和f(n ) 的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。

 2.6常见的时间复杂度

  • 1< logn < n < n < nlogn < n² < n ³ < 2^n < n! < n^n

 2.7最坏情况与平均情况

  • 最坏情况运行时间是一种保证,那就是运行时间将不会再坏了。在应用中,这是一种最重要的需求,通常,除非特别指定,我们提到的运行时间都是最坏情况的运行时间。
  • 平均运行时间是所有情况中最有意义的,因为它是期望的运行时间。

 


3.1线性表的定义:零个或多个数据元素的有限序列。

在任意时刻,线性表的长度应该小于等于数组的长度。

线性表顺序存储结构的优缺点:

  • 优点:无须为表示表中元素之间的逻辑关系而增加额外的存储空间,可以快速地存取表中任一位置的元素;
  • 缺点:插入和删除操作需要移动大量元素,当线性表长度变化较大时,难以确实存储空间的容量,造成存储空间的“碎片”。

3.2单链表的插入与删除

  • 插入:s → next = p → next , p → next =  s;
  • 删除:p → next =  p → next→ next。
  •  

3.3单链表结构与顺序存储结构优缺点

  • 存储分配方式:
  1. 顺序存储结构用一段连续的存储单元依次存储线性表的数据元素;
  2. 单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素。
  • 时间性能:
  1. 查找:顺序存储结构O(1) ,单链表O(n);
  2. 插入与删除:顺序存储结构O(n) ,单链表O(1)。
  • 空间性能:
  1. 顺序存储结构需要预分配存储空间,分大了,浪费分小了易发生上溢;
  2. 单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制。

 

3.4 静态链表

  • 静态链表:用数组描述的链表叫做静态链表
  • 静态链表优缺点:
  1. 优点:在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点。
  2. 缺点:没有解决连续存储分配带来的表长难以确定的问题,失去了顺序存储结构随机存取的特性。

3.5 循环链表

  • 循环链表:将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。

3.6双向链表


4.1 栈的定义

  • 栈:是限定仅在表尾进行插入和删除操作的线性表。
  • 两栈共享空间:当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况,比如:股票买卖。
  • 链栈的操作绝大部分都和单链表类似,只是在插入和删除上,特殊一些。

4.2栈的应用 ——递归

  • 递归:把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数。
  • 每个递归定义必须至少有一个条件,满足时递归不再进行,即不再引用自身而是返回值退出。

4.3栈的应用——四则运算表达式求值

  • 后缀表示法定义:
  1. 9 3 1 - 1* + 10 2 / +  ,从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出 栈,进行运算,运算结果进栈,一直到最终获得结果。
  • 中缀表达式转后缀表达式:
  1. 9+(3 - 1)* 3 + 10 / 2 转化为后缀表达式  9 3 1 - 1* + 10 2 / + 
  2. 规则: 从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈项符号的优先级,是右括号或优先级不高于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
  • 先将中缀表达式转化为后缀表达式,再将后缀表达式进行运算得出结果。

4.4队列的定义

  • 队列:只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

          4.5循环队列:头尾相接的顺序存储结构称为循环队列

  • 循环队列:头尾相接的顺序存储结构称为循环队列
  • 队列满的条件是: (rear + 1)% QueueSize == front
  • 计算队列长度公式为:(rear - front + QueueSize) % QueueSize

          4.6队列的链式存储结构及实现

  • 队列的链式存储结构:其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。

        5.1串的定义

  • 串:是由零个或多个字符组成的有限序列,又名叫字符串。

5.2串的存储结构

  • 串值的存储空间可在程序执行过程中动态分配而得。比如在计算机中存在一个自由存储区,叫做“堆”。这个堆可由C语言的动态分配函数malloc()和 free () 来管理。 

5.3KMP模式匹配算法(普拉特算法)


6.1树的定义

  • 树是 n ( n > = 0 ) 个结点的有限集。n = 0 时称为空树。在任意一棵非空树中:
  1. 有且仅有一个特定的称为根的结点;
  2. 当 n > 1 时,其余结点可分为 m (m > 0)个互不相交的有限集T1 、T2……Tm,其中每一个集合本身又是一棵树,并且称为根的子树。

6.2树的存储结构

  • 有三种:双亲表示法、孩子表示法和孩子兄弟表示法

6.3二叉树的定义

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

6.4特殊二叉树

  • 斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只是右子树的二叉树叫右斜树。这两者统称为斜树;
  • 满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树
  • 完全二叉树:对一棵具有 n 个 结点的二叉树按层序编号,如果编号为 i (1 <= i <= n) 的结点与同样深度的满二叉树中编号 为 i 的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树

6.5二叉树的性质

  • 在二叉树的第 i 层上至多有2^(i - 1)个结点(i >= 1)
  • 深度为k的二叉树至多有2^k - 1个结点(k >= 1)
  • 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则 n0 = n2 + 1
  • 具有 n 个结点的完全二叉树的深度为 【log 2 ^ n】+ 1(【x】表示不大于 x 的最大整数)
  • 如果对一棵有 n 个结点的完全二叉树(其深度为 【log 2 ^ n】+ 1)的结点按层序编号(从第一层到 第 【log 2 ^ n】+ 1 层,每层从左到右,对任一结点(1 <= i <= n)有:
  1. 如果 i  = 1,则结点 i 是二叉树的根,无双亲;如果i >1 ,则其双亲是结点[ i / 2];
  2.            如果 2i  >  n,则结点 i 无左孩子(结点 i 为 叶子结点); 否则其左孩子是结点2i;
  3.            如果 2i  + 1>  n,则结点 i 无右孩子;否则其右孩子是结点 2i + 1。 

 6.6二叉树的存储结构

  • 二叉链表: 二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个。 

6.7二叉树的遍历

  • 前序遍历,中序遍历,后序遍历,层序遍历

6.8线索二叉树

6.9树、森林与二叉权的转换

6.10 赫夫曼树及其应用


7.1图的定义

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

7.2各种图的定义

  • 图按照有无方向分为无向图和有向图。无向图由顶点和边构成,有向图由顶点和弧构成。弧有弧尾和弧头之分
  • 图按照边或弧的多少分稀疏图和稠密图。如果任意两个顶点之间都存在边叫完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图
  • 图中顶点之间有邻接点、依附的概念。无向图顶点的边数叫做度,有向图顶点分为入度和出度
  • 图上的边或弧上带权则称为网
  • 图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是连通图,有向则称强连通图。图中有子图,若子图极大连通则就是连通分量,有向的则称强连通分量
  • 无向图中连通且n 个顶点 n-1条边叫生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向树。一个有向图由若干棵有向树构成生成森林

7.4图的存储结构

  • 邻接矩阵:图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。(无向图的边数组是一个对称矩阵)
  • 邻接表:数组与链表相结合的存储方法称为邻接表
  • 十字链表:邻接表和逆邻接表结合起来
  • 邻接多重表:ivex和jvex是与某条边依附的两个顶点在顶点表中的下标。ilink指向依附顶点ivex的下一条边,jlink指向依附顶点jvex的下一条边。
  • 边集数组:由两个一维数组构成。一个是存储顶点的信息:另一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标、终点下标和权组成。

7.5图的遍历

  • 深度优先遍历:深度优先搜索,简称为DFS。从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
  • 广度优先遍历:广度优先搜索,简称为BFS。

7.6最小生成树

  • 定义:我们把构造连通图的最小代价生成树称为最小生成树(普里姆算法和克鲁斯卡尔算法)
  • 克鲁斯卡尔算法稀疏图效率高因为边数少,普里姆算法稠密图效率高因为边数多。

7.7最短路径

  • 迪杰斯特拉算法 和弗洛伊德算法
  • 一个点到一个点的最短路径选迪杰斯特拉算法,时间复杂度是O(n^2)
  • 所有点到所有点的最短路径选弗洛伊德算法,时间复杂度是O(n^3)

7.8拓扑排序

  • 在一个表示工和的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为AOV网
  • 拓扑排序:一个有向图构造拓扑序列的过程(需要辅助的数据结构一栈)

7.9关键路径

  • 一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE网
  • 关键路径:从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动

8.1查找概论

  • 查找表:是由同一类型的数据元素(或记录)构成的集合。
  • 关键字是数据元素中某个数据项的值,又称为键值。
  • 查找:就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素
  1. 静态查找表:只作查找操作的查找表;
  2. 动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。

8.3顺序表查找

  • 顺序查找:又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个记录开始,逐个进行记录的关键字和给定值比较,若某个记录的关键字和给定值相等,则查找成功,找到所查的记录;如果直到最后一个记录其关键字和给定值比较都不等时,则表中没有所查的记录,查找不成功。

8.4有序表查找

  • 折半查找:又称二分查找。它的前提是线性表中的记录必须是关键码有序(通常从小到大有序),线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若线定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。
  • 插值查找:根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式: key-a[low] / a[high]-a[low]
  • 斐波那契查找
  1. 当key = a[mid]时,查找就成功;
  2. 当key < a[mid]时,新范围是第low个到第mid -1个,此时范围个数为F【k - 1】- 1个;
  3. 当key > a[mid]时,新范围是第 m + 1个到第high个,此时范围个数为 F【k - 2】- 1个。

8.5线性索引查找

  • 索引:就是把一个关键字与它对应的记录相关联的过程;
  • 线性索引:就是将索引项集合组织为线性结构,也称为索引表。
  1. 稠密索引:是指在线性索引中,将数据集中的每个记录对应一个索引项,索引项一定是按照关键码有序的排列。
  2. 分块索引:分块有序,是把数据集的记录分成若干块,并且这些块需要满足两个条件。
  3. 倒排索引:其中记录号表存储具有相同次关键字的所有记录的记录号(可以是指向记录的指针或者是该记录的主关键字)

8.6二叉排序树

  • 二叉排序树:又称为二叉查找树。它或者是一棵空树,或者是具有下列性质的二叉树
  1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结构的值
  2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
  3. 它的左、右子树也分别为二叉排序树

8.7平衡二叉树(AVL树)

  • 平衡二叉树:是一种二叉排序树,其中每一个节点的左子树和右子树的高度至多等于1。
  • 平衡因子BF:二叉树上结点的左子树深度减去右子树深度的值。
  • 最小平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,我们称为最小不平衡子树

8.8多路查找树(B树)

  • 多路查找树:其每一个结点的孩子数可以多于两个,且每一个结点处可以存储多个元素。有4种特殊形式:2-3树、2-3-4树、B树和B+树。l
  • 2-3树:其中的每一个结点都具有两个孩子或三个孩子
  • 2-3-4树:其实就是2-3树的概念扩展,包括4个结点的使用。一个4结点包含小中大三个元素和四个孩子(或没有孩子)
  • B树:是一种平衡的多路查找树,结点最大的孩子数目称为B树的阶
  • B+树:是应文件系统所需而出的一种B树的变形树,注意严格意义上讲,它其实已经不是第六章定义的树了。在B树中,每一个元素在该树中只出现一次,有可能在叶子结点上,也有可能在分支结点上。而在B+树中,出现在分支结点中的元素会被当作它们在该分支结点位置的中序后继者中再次列出。另外,每一个叶子结点都会保存一个指向后一叶子结点的指针

8.9散列表查找(哈希表)概述

  • 散列技术:是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。
  • 这种对应关系f称为散列函数,又称为哈希函数。
  • 采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表。
  • 散列技术最适合的求解问题是查找与给定值相等的记录

8.10散列函数的构造方法

  • 好的散列函数:计算简单,散列地址分布均匀。
  • 直接定址法:f(key) = a x key + b (a、b为常数)
  • 数字分析法:抽取,使用关键字的一部分来计算散列存储位置的方法,这在散列函数中是常常用到的手段
  • 平方取中法:例如 1234 平方是1522756,再抽取中间的3位就是227,用做散列地址。这种方法适合于不知道关键字的分布,而位数又不是很大的情况。
  • 折叠法:将关键字从左到右分割成位数相等的几部分,然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。适合关键字位数较多的情况。
  • 除留余数法:f(key) = key mod p (p <= m),若散列表表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数
  • 随机数法:f(key) = random(key)。适合关键字长度不等时。

8.11处理散列冲突的方法

  • 开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
  • 再散列函数法:fi (key) = RHi (key) (i = 1,2,……,k)
  • 链地址法:只是在当前位置给单链表增加结点的问题
  • 公共溢出区法:给冲突的建立一个公共的溢出区来存放

9.1排序的基本概念与分类

  • 概念:假设含有n个记录的序列为{r1,r2,……,rn},其相应的关键字分别为{k1,k2,……,kn},需确定1,2,……n的一种排列p1,p2,……,pn,使其相应的关键字满足kp1 <= kp2 <= …… <= kpn 非递减(或非递增)关系,即使得序列成为一个按关键字有序的序列{rp1,rp2,……,rpn},这样的操作就称为排序。
  • 排序的稳定性:假设ki = kj (1 <= i <= n ,1 <= j <= n ,i 不等于 j)。如果排序后ri仍领先于rj,则称所用的排序方法是稳定的;反之,若可能使得排序后的序列中rj 领先 ri,则称所用的排序方法是不稳定的。
  • 内排序与外排序:内排序是在排序整个过程中,待排序的所有记录全部被放置在内存中。外排序是由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行。
  1. 时间性能 
  2. 辅助空间
  3. 算法的复杂性:内排序分为:插入排序、交换排序、选择排序和归并排序
  • 简单算法:冒泡排序、简单选择排序和直接插入排序
  • 改进算法:希尔排序、堆排序、归并排序、快速排序

9.2冒泡排序

  • 冒泡排序:一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换。

9.3简单选择排序

  • 简单选择排序法:就是通过n-i次关键字间的比较,从n - i + 1个记录中选出关键字最小的记录,并和第 i (1 <= i <= n)个记录交换之。(最大特点就是交换移动数据次数相当少,节约时间,所以略优于冒泡排序)

9.4直接插入排序

  • 直接插入排序:基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录数增1的有序表(直接插入排序法比冒泡和简单选择排序的性能要更好一些)

9.5希尔排序

  • 希尔排序:将相隔某个 增量 的记录组成一个子序列,实现跳跃式的移动,使得排序的效率提高,时间复杂度是(O(n^ 3/2))

9.6堆排序

  • 堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
  • 将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走,然后将剩余的n -1个序列重新构造成一个堆,这样就会得到 n 个元素中的次大值。如此反复执行,便能得到一个有序序列了。时间复杂度为O{nlogn}
  • 初始构建堆所需的比较次数较多,因此,它并不适合待排序序列个数较少的情况。

9.7归并排序

  • 假设初始序列含有n 个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到【n/2】个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。时间复杂度为O{n + logn}
  • 就一种比较占用内存,但却效率高且稳定的算法
  • 非递归实现归并排序:避免了递归时深度为log2n的栈空间,空间只是用到申请归并临时用的TR数组,因此空间复杂度为O{n},并且避免递归也在时间性能上有一定的提升,应该说,使用归并排序时,尽量考虑用非递归方法。时间复杂度为O{n}

9.9快速排序

  • 快速排序算法:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。时间复杂度为O{n}或O{logn}。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

10-13 18:27