软件设计师备考笔记
上午题 | 下午题 |
---|---|
计算机网络概述 | 数据流图设计(下午试题一) |
程序设计语言基础知识 | 数据库设计(下午试题二) |
标准化和知识产权 | UML分析与设计(下午试题三) |
数据库 | 面向对象程序设计与实现(下午试题六) |
操作系统 | 算法设计与C语言实现(下午试题四) |
结构化开发与方法 | |
软件工程 | |
网络与信息安全 | |
数据结构 | |
算法分析设计 |
1 线性结构
- 线性结构:每个元素最多只有一个出度和一个入度,表现为一条线状。线性表按存储方式分为顺序表和链表
- 存储结构:
- 顺序存储:用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的元素物理上也相邻
- 链式存储:存储各数据元素的结点的地址并不要求是连续的,数据元素逻辑上相邻,物理上分开
1.1 线性表
顺序存储和链式存储对比:
- 在空间方面,因为链表还需要存储指针,因此有空间浪费存在
- 在时间方面,由顺序表和链表的存储方式可知,当需要对元素进行破坏性操作(插入、删除)时,链表效率更高,因为其只需要修改指针指向即可,而顺序表因为地址是连续的,当删除或插入一个元素后,后面的其他节点位置都需要变动。而当需要对元素进行不改变结构操作时(读取、查找),顺序表效率更高,因为其物理地址是连续的,如同数组一般,只需按索引号就可快速定位,而链表需要从头节点开始,一个个的查找下去
1.1.1 单链表
单链表的插入和删除:
- 插入操作:
s->next = p->next;
p->next = s;
- 删除操作:
p->next = p->next->next;
free(p);
1.1.2 栈和队列
栈
:先进后出
,只有栈顶能进出队列
:先进先出
,分队头和队尾
1.1.3 串
- 字符串是一种特殊的线性表,其数据元素都为字符
- 空串:长度为0的字符串,没有任何字符
- 空格串:由一个或多个空格组成的串,空格是空白字符,占一个字符长度
- 子串:串中任意长度的连续字符构成的序列称为子串。含有子串的串称为主串,空串是任意串的子串
串的模式匹配算法
:子串的定位操作,用于查找子串在主串中第一次出现的位置的算法- 基本的模式匹配算法:也称为布鲁特一福斯算法,其基本思想是从主串的第1个字符起与模式串的第1个字符比较,若相等,则继续逐个字符进行后续的比较;否则从主串中的第2个字符起与模式串的第1个字符重新比较,直至模式串中每个字符依次和主串中的一个连续的字符序列相等时为止,此时称为匹配成功,否则称为匹配失败
- KMP算法:对基本模式匹配算法的改进,其改进之处在于:每当匹配过程中出现相比较的字符不相等时,不需要回溯主串的字符位置指针,而是利用已经得到的“部分匹配”结果将模式串向右“滑动”尽可能远的距离,再继续进行比较
2 数组、矩阵和广义表
2.1 数组
- 数组是定长线性表在维度上的扩展,即线性表中的元素又是一个线性表。N维数组是一种“同构”的数据结构,其每个数据元素类型相同、结构一致
- 一个m行n列的数组表示如下:
- 其可以表示为行向量形式或者列向量形式线性表,单个关系最多只有一个前驱和一个后继,本质还是线性的
- 数组结构的特点:数据元素数目固定;数据元素类型相同;数据元素的下标关系具有上下界的约束且下标有序
- 数组数据元素固定,一般不做插入和删除运算,适合于采用顺序结构
2.1.1 数组存储地址的计算
假设每个数组元素占用内存长度为len,起始地址为a,存储地址计算如下:
2.2 矩阵
特殊矩阵:矩阵中的元素(或非0元素)的分布有一定的规律。常见的特殊矩阵有对称矩阵、三角矩阵和对角矩阵
- 稀疏矩阵:在一个矩阵中,若非零元素的个数远远少于零元素个数,且非零元素的分布没有规律。存储方式为三元组结构,即存储每个非零元素的(行,列,值)
2.3 广义表
- 广义表是线性表的推广,是由0个或多个单元素或子表组成的有限序列
- 广义表与线性表的区别:线性表的元素都是结构上不可分的单元素,而广义表的元素既可以单元素,也可以是有结构的表
- 广义表一般记为:LS =(a ,az,…,αn)
- 其中LS是表名,ai是表元素,它可以是表(称为子表),也可以是数据元素(称为原子)。其中n是广义表的
长度
(也就是最外层包含的元素个数
),n=0的广义表为空表;而递归定义的重数就是广义表的深度,即定义中所含括号的重数
(单边括号的个数
,原子的深度为0,空表的深度为1) head()
:取表头
(广义表第一个表元素,可以是子表也可以是单元素)tail()
:取表尾
(广义表中,除了第一个表元素之外的其他所有表元素构成的表,非空广义表的表尾必定是一个表,即使表尾是单元素)
- 其中LS是表名,ai是表元素,它可以是表(称为子表),也可以是数据元素(称为原子)。其中n是广义表的
3 树
3.1 树与二叉树的定义
3.1.1 树
树结构是一种非线性结构,树中的每一个数据元素可以有两个或两个以上的直接后继元素,用来描述层次结构关系。
树是n个结点的有限集合(n>=0),当n=0时称为空树,在任一颗非空树中
有且仅有一个根节点;其余结点可分为m(m>=0)个互不相交的有限子集T1,T2,
…,Tm,其中每个Ti又都是一棵树,并且成为根结点的子树
3.1.2 二叉树
二叉树是n个节点的有限集合,它或者是空树,或者是由一个根节点及两颗互不相交的且分别称为左、右子树的二叉树所组成。与树的区别在于每个根节点最多只有两个孩子结点
- 二叉树的
重要特性
:- 二叉树第i层(i≥1)上最多有2i-1个结点
- 高度为k的二叉树最多有2k-1个结点(k≥1)
- 对于任何一棵二叉树,若其终端结点数为no,度为2的结点数为 n2,则n0 = n2 + 1
- 具有n个结点的完全二叉树的深度为: ⌊ 1 o g 2 n ⌋ \lfloor 1og_2n \rfloor ⌊1og2n⌋ + 1
- 对一棵有n个结点的完全二叉树的所有结点按层次自上而下、自左至右进行编号,则对于任一结点i(1 ≤ i ≤ n)有:
- 若 i = 1 ,则结点i是二叉树的根,无双亲:若 i > 1,则其双亲为: ⌊ i 2 ⌋ \lfloor \frac{i}{2} \rfloor ⌊2i⌋
- 若 2i > n ,则结点 i 没有左孩子,否则其左孩子为2i
- 若 2i + 1 > n ,则结点i没有右孩子,否则其右孩子为 2i + 1
3.2 二叉树的存储方式
3.2.1 二叉树的顺序存储结构
顺序存储,就是用一组连续的存储单元存储二叉树中的节点,按照从上到下,从左到右的顺序依次存储每个节点
- 对于深度为k的完全二叉树,除第k层外,其余每层中节点数都是上一层的两倍,由此,从一个节点的编号可推知其双亲、左孩子、右孩子结点的编号。假设有编号为 i 的节点,则有:
- 若 i = 1 ,该结点为根结点,无双亲
- 若 i ≥ 1 ,该结点的双亲为(i+1)/2(取整数)
- 若 2i≤ n ,则该结点的左孩子编号为2i,否则无左孩子。若 2i+1 ≤ n ,则该结点的右孩子编号为 2i+1 ,否则无右孩子
- 若 i 为奇数且不为 1,则该结点左兄弟的编号为 i-1 ,否则无左兄弟
- 若 i 为偶数且小于 n,则该结点右兄弟的编号为 i+1 ,否则无右兄弟
3.2.2 二叉树的链式存储结构
一般用二叉链表来存储二叉树节点,二叉链表中除了该节点本身的数据外,还存储有左孩子结点的指针、右孩子结点的指针,即 一个数据 + 两个指针。每个二叉链表节点存储一个二叉树节点,头指针则指向根节点
3.3 二叉树的遍历
- 一颗非空的二叉树由根节点、左子树、右子树三部分组成,遍历这三部分也就遍历了整颗二叉树。这三部分遍历的基本顺序是先左子树后右子树,但根节点顺序可变,以根节点访问的顺序为准有下列三种遍历方式:
先序
(前序)遍历:根左右
中序
遍历:左根右
后序
遍历:左右根
- 层次遍历:按层次,从上到下,从左到右
3.4 线索二叉树
引入线索二叉树是为了保存二叉树遍历时某节点的前驱节点和后继节点的信息,二叉树的链式存储只能获取到某节点的左孩子和右孩子结点,无法获取其遍历时的前驱和后继节点,因此可以在链式存储中再增加两个指针域,使其分别指向前驱和后继节点,但这样太浪费存储空间,考虑下述实现方法:
- 若n个节点的二叉树使用二叉链表存储,则必然有 n+1 个空指针域,利用这些空指针域来存放节点的前驱和后继节点信息,为此,需要增加两个标志,以区分指针域存放的到底是孩子结点还是遍历节点,如下:
- 若二叉树的二叉链表采用上述结构,则称为线索链表,其中指向前驱、后继节点的指针称为线索,加上线索的二叉树称为线索二叉树
3.5 最优二叉树
最优二叉树又称为哈夫曼树,是一类带权路径长度最短的树
- 路径:树中一个结点到另一个结点之间的通路
- 结点的路径长度:路径上的分支数目
树的路径长度
:根节点到达每一个叶子节点之间的路径长度之和- 权:节点代表的值
- 结点的带权路径长度:该结点到根结点之间的路径长度乘以该节点的权值
树的带权路径长度
(树的代价):树的所有叶子节点的带权路径长度之和
- 哈夫曼树的求法:给出一组权值,将其中两个最小的权值作为叶子节点,其和作为父节点,组成二叉树,而后删除这两个叶子节点权值,并将父节点的值添加到该组权值中。重复进行上述步骤,直至所有权值都被使用完。
- 构造出的哈夫曼树中,所有初始给出的权值都作为了叶子节点,此时,求出每个叶子节点的带权路径长度,而后相加,就是树的带权路径长度,这个长度是最小的
- 构造出的哈夫曼树中,所有初始给出的权值都作为了叶子节点,此时,求出每个叶子节点的带权路径长度,而后相加,就是树的带权路径长度,这个长度是最小的
3.6 树和森林
3.6.1 树的存储结构
- 双亲表示法:用一组连续的地址单元存储树的节点,并在每个节点中附带一个指示器,指出其双亲节点所在数组元素的下标
- 孩子表示法:在存储结构中用指针指示出节点的每个孩子,为树中每个节点的孩子建立一个链表
- 孩子兄弟表示法:又称为二叉链表表示法,为每个存储节点设置两个指针域,分别指向该节点的第一个孩子和下一个兄弟节点
3.6.2 树和森林的遍历
由于树中每个节点可能有多个子树,因此遍历树的方法有两种:
- 先根遍历:先访问根节点,再依次遍历根的各颗子树
- 后根遍历:先遍历根的各颗子树,再访问根节点
森林中有很多课树,森林的遍历方法也分为两种,与树的遍历类似,就是对森林中的每棵树都依次做先根遍历或后根遍历
3.6.3 树二叉树的转换
规则是:树的最左边节点作为二叉树的左子树,树的其他兄弟节点作为二叉树的右子树节点
示例如下图:采用连线法,将最左边节点和其兄弟节点都连接起来,而原来的父节点和兄弟节点的连线则断开
3.7 查找二叉树
查找二叉树上的每个节点都存储一个值,且每个节点的所有左孩子结点值都小于父节点值,而所有右孩子结点值都大于父节点值,是一个有规律排列的二义树,这种数据结构可以方便查找、插入等数据操作
二叉排序树的查找效率取决于二叉排序树的深度,对于结点个数相同的二叉排序树,平衡二叉树的深度最小,而单枝树的深度是最大的,故效率最差
3.8 平衡二叉树
在查找二叉树特点的基础上要求每个节点的平衡度只能为 0 或 1 或 -1
节点的左右子树深度就是其左右子树各自的层数,而后将左子树深度减去右子树深度,就得到了该节点的平衡度。因此,平衡二叉树就是任意左右子树层次相差不超过 1
4 图
4.1 图的相关概念
图也是一种非线性结构,图中任意两个节点间都可能有直接关系
- 无向图:图的结点之间连接线是没有箭头的,不分方向
- 有向图:图的结点之间连接线是箭头,区分A到B,和B到A是两条线
- 完全图:无向完全图中,节点两两之间都有连线,n个结点的连线数为(n-1)+(n-2)+…+1=n*(n-1)/2;有向完全图中,节点两两之间都有互通的两个箭头,n个节点的连线数为n*(n-1)
- 度、出度和入度:顶点的度是关联与该顶点的边的数目。在有向图中,顶点的度为出度和入度之和。出度是以该顶点为起点的有向边的数目。入度是以该顶点为终点的有向边的数目
- 路径:存在一条通路,可以从一个顶点到达另一个顶点,有向图的路径也是有方向的
- 连通图和连通分量:针对无向图。若从顶点v到顶点u之间是有路径的,则说明v和u之间是连通的,若无向图中任意两个顶点之间都是连通的,则称为连通图。无向图G的极大连通子图称为其连通分量
- 强连通图和强连通分量:针对有向图。若有向图任意两个顶点间都互相存在路
货即存在v到u,也存在u到v的路径,则称为强连通图。有向图中的极大连通子图称连通分量
网:边带权值的图称为网
4.2 图的存储
- 邻接矩阵:假设一个图中有n个节点,则使用n阶矩阵来存储这个图中各节点的关系,规则是若节点i到节点j有连线,则矩阵Rij=1,否则为0。因此,如果是一个无向图,肯定是沿对角线对称的,只需要存储上三角或者下三角就可以了,而有向图则不一定对称
- 邻接链表:用到了两个数据结构,先用一个一维数组将图中所有顶点存储起来,而后,对此一维数组的每个顶点元素,使用链表挂上和其有连线关系的结点的编号和权值
4.3 图的遍历
- 图的遍历是指从图的任意节点出发,沿着某条搜索路径对图中所有节点进行访问且只访问一次,分为以下两种方式:
- 深度优先遍历:从任一顶点出发,遍历到底,直至返回,再选取任一其他节点出发,重复这个过程直至遍历完整个图
- 广度优先遍历:先访问完一个顶点的所有邻接顶点,而后再依次访问其邻接顶点的所有邻接顶点,类似于层次遍历
4.4 图的最小生成树
- 假设有n个节点,那么这个图的最小生成树有 n-1 条边(不会形成环路,是树非图),这n-1条边会将所有顶点都连接成一个树,并且这些边的权值之和最小,因此称为最小生成树
- 普里姆算法:从任意顶点出发,找出与其邻接的边权值最小的,此时此边的另外一个顶点自动加入树集合中,而后再从这这个树集合的所有顶点中找出与其邻接的边权值最小的,同样此边的另外一个顶点加入树集合中,依次递归,直至图中所有顶点都加入树集合中,此时此树就是该图的最小生成树
- 克鲁斯卡尔算法(推荐):这个算法是从边出发的,因为本质是选取权值最小的n-1条边,因此,就将边按权值大小排序,依次选取权值最小的边,直至囊括所有节点,要注意,每次选边后要检查不能形成环路
4.5 图的拓扑序列
- AOV网(以顶点表示活动的网):在有向图中,以顶点表示活动,用有向边表示活动之间的优先关系
- AOV网用来表示大的工程项目执行计划,因此不能出现有向环,若存在,则意味着某项活动必须以自身任务的完成为先决条件,因此若要检测一个工程是否可行,首先应检查对应的AOV网是否存在回路。检测的方法是对有向图构造其顶点的拓扑有序序列
- 构造方法:将有向图的有向边作为活动开始的顺序若图中一个节点入度为0,则应该最先执行此活动,而后删除掉此节点和其关联的有向边,再去找图中其他没有入度的结点,执行活动,依次进行,示例如下: