数据结构与算法教程学习笔记

第 1 章 绪论

第 2 章 线性表

第 3 章 栈和队列

第 4 章 串

4.1 串类型的定义

串由零个或多个字符构成的有限序列,通常记为:s="a_0a_1\cdots a_{n-1}"(n\ge 0)

其中,s 是串名,用双引号括起来的部分(不含该双引号本身)称为串值,每个 a_i(0\le a_i <n) 为字符。串值中字符个数(也就是 n)称为串长,长度为 0 的串称为空串,各个字符全是空格字符的串(n不为0)称为空格串。

4.2 字符串的实现

4.3 字符串模式匹配算法

第 5 章 数组和广义表

5.1 数组

5.2 矩阵

5.2.3 稀疏矩阵

稀疏矩阵是 0 元素居多的矩阵,在科学和工程计算中有着十分重要的应用。如一个矩阵中有许多元素为 0,则称该矩阵为稀疏矩阵。在稀疏矩阵和稠密矩阵之间并没有一个精确的界限。假设 m 行 n 列的矩阵含 t 个非零元素,一般称 \delta =\frac{t}{m\times n} 为稀疏 因子。

5.3 广义表

5.3.1 基本概念

广义表通常简称为表,是由 n(n≥0)个元素组成的有限序列,记作:

GL=(a_1,a_2,a_3,\cdots,a_n)

广义表的例子:G=((a,(b,c)),x,(y,z)),这个广义表的表名为 G,长度为3,元素包括 (a,(b,c))、x、(y,z),其中的 x 是原子元素,(a,(b,c)) 和 (y,z) 是子表元素,它们本身又分别是一个广义表。若广义表 GL 中某元素含有广义表 GL 自身,则称 GL 为递归表。

第 6 章 树与二叉树

6.1 树的基本概念

6.2 二叉树

6.3 二叉树遍历

6.3.1 遍历的定义

先序遍历(根左右)

(1)访问根结点(D);

(2)先序遍历左子树(L);

(3)先序遍历右子树(R)。

中序遍历(左根右)

(1)中序遍历左子树(L);

(2)访问根结点(D);

(3)中序遍历右子树(R)。

后序遍历(左右根)

1)后序遍历左子树(L);

(2)后序遍历右子树(R);

(3)访问根结点(D)。

层次遍历

按照二叉树的层次,从上到下、从左至右的次序访问各结点

6.3.2 遍历算法

6.4 线索二叉树

6.4.1 线索的概念

通常称表示前驱和后继的指针叫做“线索”,而这种使树中结点的空指针成员存放前驱或后继信息的过程叫做“线索化”,加上了线索的二叉树叫做线索二叉树

6.4.2 线索二叉树的实现

6.5 树和森林

6.5.1 树的存储表示

6.5.2 树的显示

6.5.3 森林的存储表示

可以采用树的存储表示法来表示森林,只是树只有一个根结点,而森林可以有多个根结点。

6.5.4 树和森林的遍历

6.5.5 树和森林与二叉树的转换

6.6 哈夫曼树与哈夫曼编码

6.6.1 哈夫曼树的基本概念

6.6.2 哈夫曼树构造算法

6.6.3 哈夫曼编码

6.6.4 哈夫曼树的实现

6.7 树的计数

第 7 章 图

7.1 图的定义和术语

7.2 图的存储表示

7.2.1 邻接矩阵

7.2.2 邻接表

邻接表就是将矩阵中的行用链表来存储,并且只存储非 0 元素。

各边在链表中的顺序是任意的,视边的输入次序而定,在画图时通常按邻接点的编号大小排序。

7.3 图的遍历

7.3.1 深度优先搜索

7.3.2 广度优先搜索

7.4 连通无向网的最小代价生成树

7.4.1 Prim算法

从边界选最小

7.4.2 Kruskal算法

从全局选最小

7.5 有向无环图及应用

7.5.1 拓扑排序

对一个有向无环图 G=(V, {E}) 进行拓扑排序,就是将 G 中所有顶点排成一个线性序列,使得图中任意一对顶点 u 和 v,若 \left \langle u,v \right \rangle \in E,则 u 在线性序列中出现在 v 之前,这样的线性序列称为拓扑有序的序列,简称拓扑序列

AOV 图:用顶点表示活动,有向边表示活动间的优先关系,这样的有向图称为顶点表示活动的图,简称为AOV图

7.5.2 关键路径

AOE 网是一种边带有权值的有向无环图,用顶点来表示事件,边表示活动,边上的权值表示活动的持续时间

在AOE网中,有些活动可以并行地进行,最短完成时间应是从源点到汇点的最长路径长度(指路径上所有权值之和),称这样的路径为关键路径

7.6 最短路径

7.6.1 单源点最短路径问题

7.6.2 所有顶点之间的最短路径

第 8 章 查找

8.1 查找的基本概念

查找表是由同一类型的数据元素(或记录)所组成的集合。对查找表通常有如下4种操作:

(1)查询某个数据元素是否在查找表中。

(2)检索某个数据元素的各种属性。

(3)在查找表中插入一个数据元素。

(4)从查找表中删除某个元素。

前两种操作统称为“查找”的操作,如果只对查找表进行前两种“查找”的操作,也就是查找表的元素不发生变化,这样的查找表称为静态查找表

如在查找过程中同时还要插入查找表中不存在的数据元素或从查找表中删除已存在的某个数据元素,也就是查找表的数据元素要发生变化,这样的查找表称为动态查找表

8.2 静态表的查找

8.2.1 顺序查找

是从第 1 个记录开始逐个地对记录的关键字的值进行比较,如某个记录的关键字的值和给定值相等,则查找成功,返回此记录的序号;如果直到最后一个记录的关键字的值都和给定值不相等,则表示查找表中没有所查的记录,查找失败,返回 -1

8.2.2 有序表的查找

二分法查找

8.3 动态查找表

8.3.1 二叉排序树

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树。

(1)若它的左子树不空,则左子树上所有结点的关键字值均小于它的根结点的关键字值;

(2)若它的右子树不空,则右子树上所有结点的关键字值均大于它的根结点的关键字值;

(3)它的左、右子树也分别为二叉排序树。

8.3.2 二叉平衡树

二叉平衡树(二叉搜索树,AVL树)是具有以下特征的二叉排序树

(1)根的左子树和右子树的高度差的绝对值的最大值为1;

(2)根的左子树和右子树都是 AVL树。

8.3.3 B树和B+树

一棵 m 阶 B 树定义为有以下特性 m 树:

(1)根或者是一个叶子结点,或者至少有两个孩子。

(2)除了根结点以外,每个内部结点有 \left \lceil \frac{m}{2} \right \rceil 到 m 个孩子。

(3)所有叶子结点在树结构的同一层,并且不含任何信息(可看成是外部结点或查找失败的结点),因此m阶B树结构总是树高平衡的。

m阶B+树有如下特征: 

(1)每个结点的关键字个数与孩子个数相等,所有非最下层的内层结点的关键字是对应子树上的最大关键字,最下层内部结点包含了全部关键字。 

(2)除了根结点以外,每个内部结点有 \left \lceil \frac{m}{2} \right \rceil 到 m 个孩子。

(3)所有叶子结点在树结构的同一层,并不含任何信息(可看成是外部结点或查找失败的结点),因此树结构总是树高平衡的。

8.4 散列表

8.4.1 散列表的概念

我们对关键字 key 应用一个叫做散列函数的函数 H(key) 来确定具有此关键字 key 的特定数据元素是否在表中,即计算 H(key) 的值。H(key) 给出数据元素在散列表中的位置。设散列表 ht 的大小为m,0≤H(key)<m,为了确定关键字值为 key 的数据元素是否在表中,只需要在散列表中查看数据元素 ht[H(key)]。这样一个数据元素的地址通过一个函数来计算,所以数据元素并不需要按照特定的次序存放。

散列函数 H() 将关键字 key 对应为一个整数,满足 0≤H(key)<m,两个关键字 key1 和 key2,如果 key1≠key2,H(key1)=H(key2),也就是不同关键字得相同的散列地址,这种现象称为冲突,这时 key1与 key2 称为同义词。 选择一个散列函数时,考虑的主要因素是:

(1)选择一个易于计算的散列函数。

(2)尽量减少冲突发生的次数。

8.4.2 构造散列函数的方法

平方取中法

先计算关键字的平方,然后用结果的中间几位来获得散列表元素的地址

除留余数法

用关键字 key 除以不大于散列表大小的数 p 的一个余数,此余数表示 key 在 ht 中的地址,也就是:H(key)= key%p,为减少冲突,在一般情况下,p 最好为素数或不包含小于20的素数因子的合数。

随机数法

取关键字的随机函数值为它的散列地址,也就是 H(key)= Random(key),其中 Random 为伪随机函数,其取值在 0 到 m-1 之间。

8.4.3 处理冲突的方法

开放定址法

设散列地址为 0 ~ m-1,冲突是指关键字 key 得到的地址为 h 的位置上已存放有数据元素,处理冲突的方法就是为此关键字寻找另一个空的散列地址,处理冲突的过程可能得到一个地址序列: h_1,h_2,\cdots,h_k 也就是在处理冲突时,得到另一个散列地址 h_1,如果 h_1 还有冲突,则求下一个散列地址 h_2,依此类推,直到 h_k 不发生冲突为止。

开放定址法的一般形式为:

其中 H(key) 为散列函数,m为表长,d_i 为增量序列,d_i 有两种常见的取法: 

(1)d_i=i 也就是 d_i=1,2,\cdots,m-1,这种取法称为线性探测法。

(2)d_i=随机数,这种取法称为随机探测法。 

链地址法

散列表 ht 是一个指针数组,对于每个 h,0≤h<m,ht[h] 是指向链表的一个指针。对数据元素中的每个关键字 key,首先计算 h= H(key),然后将含此关键字的数据元素插入到 ht[h] 指向的链表中,所以对于不相同的关键字 key1 和 key2,如果 H(key1)=H(key2),带有关键字 key1 和 key2 的数据元素将被插入到相同的链表,使得冲突的处理更为快捷和高效。

8.4.4 散列表的实现

第 9 章 排序

9.1 概述

对于可以重复出现的关键字,则排序结果可能不唯一,假如对于任意 key_i=key_j(0\le i<j\le n-1),排序前数据元素 e_i 在 e_j 的前面,如果排序后数据元素 e_i 也在 e_j 的前面,这样的排序方法称为稳定的;反之如可能排序后数据元素 e_i 在 e_j 的后面,则称所用的排序方法是不稳定的。

内部排序:待排序的数据元素全部存入计算机的内存中,在排序过程中不需要访问外存。

外部排序:待排序的数据元素不能全部装入内存,在排序过程中需要不断访问外存。

9.2 插入排序

9.2.1 直接插入排序

将第 1 个数据元素看成是一个有序子序列,再依次从第 2 个数据元素起逐个插入到这个有序的子序列中。一般地,在第 i 步上,将 elem[i] 插入到由 elem[0]~elem[i-1] 构成的有序子序列中。

例如,已知待排序的一组数据元素序列 {18,8,56,9,68,8}

首先可假设有序子序列中只包含一个数据元素,也就是 {18}

将 8 插入在有序子序列中,由于 8<18,显然插入后的有序子序列为: {8,18} 

然后再将 56 插入到上述有序子序列中,由于 18<56,可知插入后的新有序子序列如下: {8,18,56}

按这种过程继续下去,直到有序子序列的数据元素个数与原序列数据元素个数相等为止。

9.2.2 Shell排序

先将整个待排序数据元素序列分割成若干子序列,分别对各子序列进行直接插入排序,等整个序列中的数据元素“基本有序”时,再对全体数据元素进行一次直接插入排序。

9.3 交换排序

9.3.1 起泡排序

9.3.2 快速排序

任选序列中的一个数据元素(通常选取第1个数据元素)作为枢轴,以它和所有剩余数据元素进行比较,将所有较它小的数据元素都排在它前面,将所有较它大的数据元素都排在它之后,经过一趟排序后,可按此数据元素所在位置为界,将序列划分为两个部分,再对这两个部分重复上述过程直至每一部分中只剩一个数据元素为止。

9.4 选择排序

选择排序的基本思想是每一趟在 n-i(i=1,2,…,n-1) 个数据元素 (elem[i], elem[i+1], …, elem[n-1]) 中选择最小数据元素作为有序序列中第 i 个数据元素。

9.4.1 简单选择排序

简单选择排序的第 i 趟是从 (elem[i], elem[i+1], …, elem[n-1]) 选择第 i 小的元素,并将此元素放到 elem[i] 处,也就是说简单选择排序是从未排序的序列中选择最小元素,接着是次小的,依此类推,为寻找下一个最小元素,需检索数组整个未排序部分,但只一次交换即将待排序元素放到正确位置上。

9.4.2 堆排序

对于有 n 个元素的序列 (elem[0], elem[1], …, elem[n-1]),当且仅当满足如下条件时,称为堆:

上面第 1 组关系定义的堆称为小顶堆,第 2 组关系定义的堆称为大顶堆

如将序列对应的数组看成是完全二叉树,则堆的定义表明完全二叉树所有非终端结点的值均不大于(或不小于)其左、右孩子的值

对于大顶堆,堆顶元素最大,在输出堆顶元素后,如果能使剩下的 n-1个元素重新构建成一个堆,则可得到次大的元素,如此继续可得到一个有序序列,这种排序方法称为堆排序。 

实现堆排序需要实现如下的算法:

(1)将一个无序序列构建成一个堆。

(2)在输出堆顶元素后,调整剩余元素成为一个新的堆。 

设输出堆顶元素后,以堆中最后一个元素代替,这时根结点的左、右子树是大顶堆,需自上而下进行调整,首先以堆顶元素的最大的左、右孩子之值进行比较,由于最大孩子为左孩子,左孩子之值90大于双亲的值36,将 36 和 90进 行交换,由36代替 90后破坏了左子树的堆特性,需进行同上面相似的调整,直至叶结点或遇到调整后仍是堆时为止。

9.5 归并排序

归并是指将两个有序子序列合并为一个新的有序子序列

设在初始序列中有n个元素,归并排序的基本思想是,将序列看成 n 个有序的子序列,每个序列的长度为1,然后两两归并,得到 \left \lceil \frac{m}{2} \right \rceil 个长度为 2 或 1 的有序子序列,然后两两归并……这样重复下去,直到得到一长度为 n 的有序子序列,这种排序方法称为2-路归并排序

如果每次将3个有序子序列合并为一个新的有 序序列,则称为3-路归并排序

对于内部排序来讲,2-路归并排序就能完全满足实现需要,只有外部排序才需要多路归并

9.6 基数排序

9.6.1 多关键字排序

假设有 n个元素序列:\left \{ elem_0,elem_1,\cdots,elem_{n-1} \right \}

元素 elem_i 含有 d 个关键字 \left ( K^0_i,K^1_i,\cdots,K^{d-1}_i \right )

其中 K^0_i 称为最主位关键字,K^{d-1}_i 称为最次位关键字

如果对任意两个元素 elem_i 和 elem_j(0\le i<j\le n-1) 都满足:

则称序列按关键字 \left ( K^0,K^1,\cdots,K^{d-1} \right ) 有序。 

多关键字序列的排序最常见的方法是最低位优先法,先对最低位 K^{d-1} 进行排序,再对高一位关键字 K^{d-2} 进行排序,依此类推,直到对 K^0 进行排序为止

9.6.2 基数排序

基数排序的本质是借助于“分配”和“收集”算法对单关键字进行排序,基数排序是将关键字 K_i 在逻辑上看成是 d 个关键字 \left ( K^0_i,K^1_i,\cdots,K^{d-1}_i \right ),如 K^j_i(0\le j\le d-1) 有 radix 种值,称 radix 为基数,例如关键字是整数,并且关键字取值范围是 0≤j≤99,则可认为关键字 K 由 2 个关键字 \left ( K^0,K^1 \right ) 组成,其中 K^0 是十位数,K^1 是个位数,并且每位关键字可取 10 个值,即基数 radix=10,在算法实现时可将数据按关键字“分配”到线性链表中,然后再对所得的线性链表进行“收集”。

9.7 各种内部怕排序方法讨论

9.8 外部排序

9.8.1 外部排序基础

9.8.2 外部排序的方法

第 10 章 文件

第 11 章 算法设计与分析

11.1 算法设计

11.1.1 递归算法

直接或间接地调用自身的算法称为递归算法,直接或间接地调用自身的函数称为递归函数。

11.1.2 分治算法

分治算法与软件设计的模块化方法类似。为了解决一个大的问题,将一个规模为 n 的问题分解为规模较小的子问题,这些子问题一般和原问题相似。分别解这些子问题,最后将各个子问题的解合并得到原问题的解。子问题通常与原问题相似,可以递归地使用分治策略来解决。

11.1.3 回溯算法

回溯法可以系统地搜索一个问题的所有解。包含问题的所有解的解空间一般组织成树,也可以组织成图结构,按照先根遍历策略(解空间为树)或深度优先策略(解空间为图),从根结点出发搜索解空间树或从某结点出发搜索解空间图。算法搜索至解空间树(或图)的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对该结点的系统搜索,逐层向其祖先结点回溯,否则,继续按先根遍历策略或深度优先策略进行搜索。回溯法用来求问题的所有解时,要回溯到根(或起始结点),且根结点的所有子树(或从起始结点出发的所有路径)都已被搜索过才结束。

11.2 算法分析

11.2.1 递归分析

11.2.2 利用生成函数进行分析

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值