数据结构与算法基础
┏┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅☆
┇ 根据B站视频以及教材做的笔记 ╹◡╹
┇ 视频:https://www.bilibili.com/video/av19665344
┇ 教材:软件设计师考试冲刺(习题与解答)
┗┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅☆
课程内容摘要
数组与矩阵
数组
如下,a[2][3]代表a[0]这一行和a[1]这一行已经存满,两行共有10个元素。a[2]这一行有0、1、2、3四个元素。所以存储地址是a+(2×5+3)×2。
a[2][3]是数组中的第14个元素,由于编号是由0开始算的,所以有13个偏移量,所以存储地址就是a+13×2。
a[0][0]的存储地址就是a。
稀疏矩阵
如下图,该矩阵为9×9的数组。
考试时可用代入法。A[0][0]的存储地址应为M[1],所以排除B和C。A[1][1]的存储地址应为M[3],所以排除D。答案选A。
数据结构的定义
- 数据结构:计算机存储以及组织数据的方式。
- 非线性结构:分为树型结构和图。图有环路,树不能有环路。
- 从广义来讲,图可以包含树,树可以包含线性结构。
线性表
线性表是线性结构的基本表现。
线性表的定义
线性表的分类
- 顺序表 :开辟了连续的空间,顺次的把表存入,采用了一维数组的方式存储信息。
- 链表:每一个存储单元包含了存数据的地方和存指针的地方。
链表的基本操作
- 单链表删除结点:p→next = q→next。
- 单链表插入结点:s→next = p→next;p→next = s。
顺序存储和链式存储对比
队列与栈
- 队列:先进先出。
- 栈:先进后出。
习题:元素按照a、b、c的次序进入栈,请尝试写出其所有可能的出栈序列。
可能的出栈序列:abc;acb;bac;bca;cba。
A选项是从左端依次输入e1、e2、e3、e4。
B选项是从任意端输入e1,再从左端输入e2,再从右端输入e3,最后从左端输入e4。
C选项是从任意端输入e1,再从右端输入e2,再从左端输入e3、e4。
D选项不符合题目要求。答案选D
广义表
- 深度:嵌套的次数。
- 表头:最外层的第一个表元素。
- 表尾:除了第一个表元素以外的其他所有元素。
例1,有广义表LS1 = (a , (b,c) , (d,e) ),则其长度为?深度为?
如上,LS1的长度为3。深度即包含括号的层数,所以深度为2。
例2,有广义表LS1 = (a , (b,c) , (d,e) ),要将其b字母取出,操作为?
tail(LS1) ,该操作取出((b,c) , (d,e));
head ( tail(LS1) ) ,该操作取出(b,c);
head ( head ( tail(LS1) ) ),该操作取出b。
树与二叉树
树
- 结点的度:一个结点拥有的孩子结点数。如下图,结点1的度为2,结点3的度为1,结点7的度为0。
- 树的度:一个树中的最高结点。如下图,最高结点为2,所以树的度为2。
- 叶子结点:无孩子结点。如下图的结点4、5、7、8。
- 分支结点:如下图的结点1、2、3、6。
- 内部结点:既非叶子结点,也非根节点。如下图的结点2、3、6。
- 父结点:相对子结点而言。
- 子结点:相对父结点而言。
- 兄弟结点:如下图的2和3、4和5、7和8。
- 层次:如下图右端的1、2、3、4层。
二叉树
二叉树的重要特性
- 满二叉树:无缺失部分。
- 完全二叉树:除了最底下一层,上面的层次都是满的树,而底下的结点是从左到右排列的,左边的结点是满的,右边的结点缺失。
二叉树遍历
- 前序遍历:根左右。如下图遍历结果是1 2 4 5 7 8 3 6。
- 中序遍历:左根右。如下图遍历结果是4 2 7 8 5 1 3 6。
- 后序遍历:左右根。如下图遍历结果是4 8 7 5 2 6 3 1。
- 层次遍历:按层次依次遍历。如下图遍历结果是1 2 3 4 5 6 7 8。
反向构造二叉树
- 如下,前序遍历的顺序是根左右,所以根节点为A。中序遍历的顺序是左根右,因此左子树结点为HBEDF,右子树结点为GC。
- 左子树结点在前序遍历中,第一个访问到的结点为B,因此B为左子树结点的根结点。同理至中序序列中,H为B的左子树结点,EDF为B的右子树结点。
- EDF在前序序列中,F为第一个访问到的结点,所以F为根结点,ED为左子树结点。进一步得出D为根结点,E为左子树结点。
树转二叉树
查找二叉树
提高查询的速度和效率。
最优二叉树(哈夫曼树)
- 哈夫曼树 是一种工具,用于哈夫曼编码。
- 哈夫曼编码 是一种压缩编码方式,能够让原始信息的编码长度变得更短,从而节省存储空间和传输带宽。属于无损压缩方式。
- 树的路径长度:树中路径的累加。
- 权:某个叶子结点的数值代表某一种字符出现的频度。
- 带权的路径长度:路径长度 × 权值。
- 树的带权路径长度(树的代价):所有叶子结点的带权路径长度之和。
构造哈夫曼树,需要将树的带权路径长度取最小的情况。
如上,首先选出权值最小的3和5构成一棵最小子树,从权值中划去3和5,添上一个8;再从中选出权值最小的7和8,从权值中划去7和8,添上一个15;再从中选出权值最小的8和11,从权值中划去8和11,添上一个19;以此类推。该树的带权路径长度为5×5+3×5+7×4+14×3+29×2+8×3+11×3+23×2。
线索二叉树
- 左子树结点的指针 指向当前结点的遍历中的前序结点。
- 右子树结点的指针 指向当前结点的遍历中的后序结点。
平衡二叉树
- 排序二叉树:即查找二叉树。
- 平衡度:左子树深度 - 右子树深度。
图
基本概念
图的存储
邻接矩阵
邻接表
用数组记录各个结点,再用链表记录每个结点可以到达的顶点情况。
图的遍历
如上图,广度遍历为V0,V4,V3,V1,V6,V2,V5,V7;深度遍历为V0,V4,V6,V7,V1。
拓扑排序
图的最小生成树
把图中很多的线和边去掉,只留权值最小的边,把所有结点连贯起来,从而形成权值相加最小的树。
普里姆算法
从一个任意结点出发,找出该结点与其他结点最短距离的边,依次找下去。
克鲁斯卡尔算法
找出最短的路径。
算法基础
算法的特性
算法的复杂度
所有常数级复杂度都记为O(1)。
查找
顺序查找
- 时间复杂度:O(n)
二分查找法
- 前提:要查找关键字的序列是有序排列的,从小到大或从大到小排序。
- 时间复杂度:O(log2n)
- 折半查找在查找成功时关键字的比较次数最多为⌊log2n⌋+1次
mid=(low+high)÷2(取整)
散列表
按内容存储。
散列表冲突的解决方法:线性探测法,伪随机数法。
排序
直接插入排序
- 时间复杂度
平均情况:O(n2)
最坏情况:O(n2) - 空间复杂度:O(1)
- 稳定性:稳定
希尔排序
最后一步进行直接插入排序。
- 时间复杂度
平均情况:O(n1.3)
最坏情况:O(n2) - 空间复杂度:O(1)
- 稳定性:不稳定
直接选择排序
依次选出最小元素与最前交换。
- 时间复杂度
平均情况:O(n2)
最坏情况:O(n2) - 空间复杂度:O(1)
- 稳定性:不稳定
堆排序
- 时间复杂度
平均情况:O(nlog2n)
最坏情况:O(nlog2n) - 空间复杂度:O(1)
- 稳定性:不稳定
堆的概念
- 小顶堆:所有的孩子结点都比自身结点大。
- 大顶堆:所有的孩子结点都比自身结点小。
基本思想
初建堆
- 首先按完全二叉树方式建堆,其次着手于最后一个非叶子结点。
- 如下图,非叶子结点有1、3、4、5。
- 从5开始调整,看5是否大于它的两个孩子,由于8>5,所以5和8进行交换。
- 然后调整4,由于6>4,所以4和6进行交换。
- 然后再调整3,由于8>3,所以3和8进行交换,又由于5>3,所以3和5再进行交换。
- 以此类推。
如上,先将顺序表按照完整二叉树方式排列。
取走堆顶元素80,将最后一个结点20放于堆顶,再进行调整。
循环进行该操作:取走堆顶元素,再将最后一个结点放于堆顶,进行调整。
冒泡排序
将最后的元素依次向前对比。
- 时间复杂度
平均情况:O(n2)
最坏情况:O(n2) - 空间复杂度:O(1)
- 稳定性:稳定
快速排序
- 时间复杂度
平均情况:O(nlog2n)
最坏情况:O(n2) - 空间复杂度:O(log2n)
- 稳定性:不稳定
归并排序
两两合并排序。
- 时间复杂度
平均情况:O(nlog2n)
最坏情况:O(nlog2n) - 空间复杂度:O(n)
- 稳定性:稳定
基数排序
按照个十百位进行收集再排序。
- 时间复杂度
平均情况:O(d(r+n))
最坏情况:O(d(r+n)) - 空间复杂度:O(r+n)
- 稳定性:稳定