前言
由于笔记复制到CSDN样式失效,没有精力再重新完整的检查并设置一遍样式,有积分的可以前往下载word、pdf、有道云笔记版本。
需要说明的是,下载的内容与本篇分享内容一致,只有样式的区别【比如重点记忆、常考内容有颜色、字号、自重等样式,目录结构更完善,表格不是图片,等】
本章下载地址:
https://download.csdn.net/download/chengsw1993/85505572
如果发现文章存在阅读不同、显示异常等内容,请评论区告知以便修改,应该都是CSDN的markdown语法导致的。
系列文章
上一篇:【软考-软件设计师精华知识点笔记】第二章 程序设计语言基础知识
下一篇:【软考-软件设计师精华知识点笔记】第四章 操作系统知识
数据结构
线性结构
线性结构:每个元素最多只有一个出度和一个入度,表现为一条线状。线性表按存储方式分为顺序表和链表。
存储结构:
顺序存储:用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的元素物理上也相邻。
链式存储:存储各数据元素的结点的地址并不要求是连续的,数据元素逻辑上相邻,物理上分开。
线性表
顺序存储和链式存储性能对比:
单链表
在上图中p所指向的节点后插入s所指向的节点,操作为:
s->next=p->next;
p->next=s;
同理,在单链表中删除p所指向节点的后继节点q时,操作为:
p->next=p->next->next;
free(q);
栈和队列
循环队列中,头指针指向第一个元素,尾指针指向最后一个元素的下一个位置,因此,当队列空时,head=tail,当队列满时,head=tail,这样就无法区分了,因此,一般将队列少存一个元素,这样,队列满时的条件就变为tail+1=head,而考虑是循环队列,必须要除以最大元素数来取余数,即(tail+1)%size=head,如上图右边所示两个公式。循环队列的长度公式为(Q.tail-Q.head)%size。
优先队列:元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。使用堆来存储,因为其不是按照元素进队列的顺序来决定的。
串
字符串是一种特殊的线性表,其数据元素都为字符。
空串:长度为0的字符串,没有任何字符。
空格串:由一个或多个空格组成的串,空格是空白字符,占一个字符长度。
子串:串中任意长度的连续字符构成的序列称为子串。含有子串的串称为主串,空串是任意串的子串。
串的模式匹配算法:子串的定位操作,用于查找子串在主串中第一次出现的位置的算法。
基本的模式匹配算法:也称为布鲁特 - 福斯算法,其基本思想是从主串的第1个字符起与模式串的第1个字符比较,若相等,则继续逐个字符进行后续的比较;否则从主串中的第2个字符起与模式串的第1个字符重新比较,直至模式串中每个字符依次和主串中的一个连续的字符序列相等时为止,此时称为匹配成功,否则称为匹配失败。
KMP算法:对基本模式匹配算法的改进,其改进之处在于:每当匹配过程中出现相比较的字符不相等时,不需要回溯主串的字符位置指针,而是利用己经得到的“部分匹配”结果将模式串向右“滑动”尽可能远的距离,再继续进行比较。
数组、矩阵和广义表
矩阵
特殊矩阵:矩阵中的元素(或非0元素)的分布有一定的规律。常见的特殊矩阵有对称矩阵、三角矩阵和对角矩阵。
稀疏矩阵:在一个矩阵中,若非零元素的个数远远少于零元素个数,且非零元素的分布没有规律。存储方式为三元组结构,即存储每个非零元素的(行,列,值)。
广义表
广义表是线性表的推广,是由0个或多个单元素或子表组成的有限序列。
广义表与线性表的区别:线性表的元素都是结构上不可分的单元素,而广义表的元素既可以单元素,也可以是有结构的表。
广义表一般记为:LS=(α1,α2,…,αn)
其中LS是表名,αi是表元素,它可以是表(称为子表),也可以是数据元素(称为原子)。其中n是广义表的长度(也就是最外层包含的元素个数),n=0的广义表为空表;而递归定义的重数就是广义表的深度,即定义中所含括号的重数(单边括号的个数,原子的深度为0,空表的深度为1)。
head()和tail():取表头(广义表第一个表元素,可以是子表也可以是单元素)和取表尾(广义表中,除了第一个表元素之外的其他所有表元素构成的表,非空广义表的表尾必定是一个表,即使表尾是单元素)操作。
树与二叉树
树结构是一种非线性结构,一个前驱多个后继
树是n个结点的有限集合(n>=0),n=0叫空树,任一非空树中,有且仅有一个根节点
二叉树
结点最多只有两个孩子。
满二叉树:每层都是满结点
完全二叉树:n-1层是满结点的,第n层从左到右是满的
二叉树的存储结构
顺序存储,就是用一组连续的存储单元存储二叉树中的节点,按照从上到下,从左到右的顺序依次存储每个节点。
对于深度为k的完全二叉树,除第k层外,其余每层中节点数都是上一层的两倍,由此,从一个节点的编号可推知其双亲、左孩子、右孩子结点的编号。假设有编号为i的节点,则有:
- 若=1,该结点为根结点,无双亲。
- 若>1,该结点的双亲为(i+1)/2(取整数)。
- 若2i<=n,则该结点的左孩子编号为2i,否则无左孩子。
- 若2i+1<=n,则该结点的右孩子编号为2+1,否则无右孩子。
- 若i为奇数且不为1,则该结点左兄弟的编号为i-1,否则无左兄弟。
- 若i为偶数且小于n,则该结点右兄弟的编号为i+1,否则无右兄弟。
二叉树的链式存储结构:一般用二叉链表来存储二叉树节点,二叉链表中除了该节点本身的数据外,还存储有左孩子结点的指针、右孩子结点的指针,即一个数据+两个指针。每个二叉链表节点存储一个二叉树节点,头指针则指向根节点。
二叉树的遍历
一颗非空的二叉树由根节点、左子树、右子树三部分组成,遍历这三部分,也就遍历了整颗二叉树。这三部分遍历的基本顺序是先左子树后右子树,但根节点顺序可变,以根节点访问的顺序为准有下列三种遍历方式:
- 先序(前序)遍历:根左右。
- 中序遍历:左根右。
- 后序遍历:左右根。
还有层次遍历方法:
- 层次遍历:按层次,从上到下,从左到右。
示例:
层次:12345678 前序:12457836 中序:42785136 后续:48752631
线索二叉树
引入线索二叉树是为了保存二叉树遍历时某节点的前驱节点和后继节点的信息,二叉树的链式存储只能获取到某节点的左孩子和右孩子结点,无法获取其遍历时的前驱和后继节点,因此可以在链式存储中再增加两个指针域,使其分别指向前驱和后继节点,但这样太浪费存储空间,考虑下述实现方法:
若n个节点的二叉树使用二叉链表存储,则必然有n+1个空指针域,利用这些空指针域来存放节点的前驱和后继节点信息,为此,需要增加两个标志,以区分指针域存放的到底是孩子结点还是遍历节点,如下:
若二叉树的二叉链表采用上述结构,则称为线索链表,其中指向前驱、后继节点的指针称为线索,加上线索的二叉树称为线索二叉树。
最优二叉树
最优二叉树又称为哈夫曼树(翻译不同也可叫霍夫曼树),是一类带权路径长度最短的树,相关概念如下:
路径:树中一个结点到另一个结点之间的通路。
结点的路径长度:路径上的分支数目。
树的路径长度:根节点到达每一个叶子节点之间的路径长度之和。
权:节点代表的值。
结点的带权路径长度:该结点到根结点之间的路径长度乘以该节点的权值。
树的带权路径长度(树的代价):树的所有叶子节点的带权路径长度之和。
哈夫曼树的求法:给出一组权值,将其中两个最小的权值作为叶子节点,其和作为父节点,组成二叉树,而后删除这两个叶子节点权值,并将父节点的值添加到该组权值中。重复进行上述步骤,直至所有权值都被使用完,左边节点小右边节点大,左分支为0右分支为1。
构造出的哈夫曼树中,所有初始给出的权值都作为了叶子节点,此时,求出每个叶子节点的带权路径长度,而后相加,就是树的带权路径长度,这个长度是最小的。
哈夫曼编码:构造完成哈夫曼树后,根据从根节点开始,根据分支编码到叶子结点的编码顺序就代表该叶子结点的编码。
数和森林
森林:两颗及以上棵树在一起就叫森林
树的存储结构【了解】
双亲表示法:用一组连续的地址单元存储树的节点,并在每个节点中附带一个指示器,指出其双亲节点所在数组元素的下标。
孩子表示法:在存储结构中用指针指示出节点的每个孩子,为树中每个节点的孩子建立一个链表。
孩子兄弟表示法:又称为二叉链表表示法,为每个存储节点设置两个指针域,分别指向该节点的第一个孩子和下一个兄弟节点。
树和森林的遍历【了解】
由于树中每个节点可能有多个子树,因此遍历树的方法有两种:
先根遍历:先访问根节点,再依次遍历根的各颗子树。
后根遍历:先遍历根的各颗子树,再访问根节点。
森林中有很多课树,森林的遍历方法也分为两种,与树的遍历类似,就是对森林中的每棵树都依次做先根遍历或后根遍历。
树和二叉树的转换
规则是:树的最左边节点作为二叉树的左子树,树的其他兄弟节点作为二叉树的右子树节点。
示例如下图:采用连线法,将最左边节点和其兄弟节点都连接起来,而原来的父节点和兄弟节点的连线则断开,这种方法最简单,要求掌握。
查找二叉树
查找(排序)二叉树上的每个节点都存储一个值,且每个节点的所有左孩子结点值都小于父节点值,而所有右孩子结点值都大于父节点值,是一个有规律排列的二叉树,这种数据结构可以方便查找、插入等数据操作。使用中序遍历结果是从小到大排列的
排序二叉树的查找效率取决于排序二叉树的深度,对于结点个数相同的二叉排序树,平衡二叉树的深度最小;而单枝树的深度是最大的,故效率最差。
平衡二叉树
前面讲过查找(排序)二叉树,特点是所有左子树值小于根节点值,所有右子树值大于根节点值,而这个特点可以构造出多个不同的二叉树,并不唯一,因此提出平衡二叉树的概念,在查找二叉树特点的基础上,要求每个节点的平衡度只能为0或1或-1.
节点的左右子树深度就是其左右子树各自的层数,而后将左子树深度减去右子树深度,就得到了该节点的平衡度。因此,平衡二叉树就是任意左右子树层次相差不超过1
例(2013年下半年):59.某二叉树的先序遍历列为cabfedg,中序遍历序列为abcdefg,则二叉树是(C)。
A.完全二叉树 B.最优二叉树 C.平衡二叉树 D.满二叉树
解析:依据先序遍历和中序遍历可以逆问构造出这颗二叉树,构造时,前序遍历可以确定根节点,中序遍历用来确定根节点的左子树节点和右子树节点;由该树可知,肯定不是AD,没有权值,也无关最优,只能是平衡二叉树,因为其任意左右子树深度相减绝对值都<=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的路径,则称为强连通图。有向图的极大强连通子图,称为强连通分量。
网:边带权值的图称为网。
注:带权的图是指每条边带上权值的图,常用于表示通路的代价。
图的存储
邻接矩阵:假设一个图中有n个节点,则使用n阶矩阵来存储这个图中各节点的关系,规则是若节点i到节点j有连线,则矩阵Ri,j=1,否则为0。因此,如果是一个无向图,肯定是沿对角线对称的,只需要存储上三角或者下三角就可以了,而有向图则不一定对称。示例如下图所示:
邻接链表:用到了两个数据结构,先用一个一维数组将图中所有顶点存储起来,而后,对此一维数组的每个顶点元素,使用链表挂上和其有连线关系的结点的编号和权值,示例如下图所示:
例题:
图的基本存储结构有邻接矩阵表示法和邻接链表表示法。图中的顶点数决定了邻接矩阵的阶和邻接表中的单链表数目,无论是对有向图还是无向图,边数的多少决定了单链表中的结点数,而不影响邻接矩阵的规模,因此完全图适合采用邻接矩阵存储。
图的遍历
图的遍历是指从图的任意节点出发,沿着某条搜索路径对图中所有节点进行访问且只访问一次,分为以下两种方式:
深度优先遍历:从任一顶点出发,遍历到底,直至返回,再选取任一其他节点出发,重复这个过程直至遍历完整个图;
广度优先遍历:先访问完一个顶点的所有邻接顶点,而后再依次访问其邻接顶点的所有邻接顶点,类似于层次遍历。
图的最小生成树
假设有n个节点,那么这个图的最小生成树有n-1条边(不会形成环路,是树而非图),这n-1条边会将所有顶点都连接成一个树,并且这些边的权值之和最小,因此称为最小生成树。
普里姆算法Prim(了解):从任意顶点出发,找出与其邻接的边权值最小的,此时此边的另外一个顶点自动加入树集合中,而后再从这个树集合的所有顶点中找出与其邻接的边权值最小的,同样此边的另外一个顶点加入树集合中,依次递归,直至图中所有顶点都加入树集合中,此时此树就是该图的最小生成树。
克鲁斯卡尔算法Kruscal(推荐):这个算法是从边出发的,因为本质是选取权值最小的n-1条边,因此,就将边按权值大小排序,依次选取权值最小的边,直至囊括所有节点,要注意,每次选边后要检查不能形成环路。
上述两个算法都是先考虑局部最优,是贪心法算法;两个算法得到的最小生成树的权重大小相同,但生成树不一定一样,由描述可知,Kruscal与边数相关,边越多越不利于选取,因此,当网稠密时,使用Prim算法更好,两个算法不存在绝对的哪个效率更高。
图的拓扑序列
AOV网(以顶点表示活动的网):在有向图中,以顶点表示活动,用有向边表示活动之间的优先关系。
AOV网用来表示大的工程项目执行计划,因此不能出现有向环,若存在,则意味着某项活动必须以自身任务的完成为先决条件,因此,若要检测一个工程是否可行,首先应检查对应的AOV网是否存在回路。检测的方法是对有向图构造其顶点的拓扑有序序列。
构造方法:将有向图的有向边作为活动开始的顺序,若图中一个节点入度为0,则应该最先执行此活动,而后删除掉此节点和其关联的有向边,再去找图中其他没有入度的结点,执行活动,依次进行,示例如下:
上图从1或6开始都可以,因此拓扑序列也不一定是唯一的